diff --git a/LICENSE.md b/LICENSE.md index 54c95918..da15cafb 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -11,7 +11,7 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -**License for USB controller drivers: src/drivers/usb/synopsys** +**License for USB controller drivers: src/drivers/usb/synopsys_otg** Apache License @@ -67,6 +67,36 @@ You may add Your own copyright statement to Your modifications and may provide a END OF TERMS AND CONDITIONS +**License for USB controller header: src/drivers/usb/synopsys_drd_regs.h** + +Copyright 2016 The Fuchsia Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **License for Apple provided components**: (apple-include, src/lib/libDER) diff --git a/Makefile b/Makefile index 1cdf96c1..5734c116 100644 --- a/Makefile +++ b/Makefile @@ -53,11 +53,11 @@ RA1N := $(ROOT)/checkra1n/kpf # General options EMBEDDED_LD_FLAGS ?= -nostdlib -static -Wl,-fatal_warnings -Wl,-dead_strip -Wl,-Z $(EMBEDDED_LDFLAGS) -EMBEDDED_CC_FLAGS ?= --target=arm64-apple-ios12.0 -Wall -Wunused-label -Werror -O3 -flto -ffreestanding -U__nonnull -nostdlibinc -I$(LIB)/include $(EMBEDDED_LD_FLAGS) $(EMBEDDED_CFLAGS) +EMBEDDED_CC_FLAGS ?= --target=arm64-apple-ios12.0 -mcpu=cyclone -mtune=cyclone -Wall -Wunused-label -Werror -O3 -ffreestanding -U__nonnull -nostdlibinc -I$(LIB)/include $(EMBEDDED_LD_FLAGS) $(EMBEDDED_CFLAGS) # Pongo options PONGO_LDFLAGS ?= -L$(LIB)/lib -lc -lm -Wl,-preload -Wl,-no_uuid -Wl,-e,start -Wl,-order_file,$(SRC)/sym_order.txt -Wl,-image_base,0x100000000 -Wl,-sectalign,__DATA,__common,0x8 -Wl,-segalign,0x4000 -PONGO_CC_FLAGS ?= -DPONGO_VERSION='"$(PONGO_VERSION)"' -DAUTOBOOT -DPONGO_PRIVATE=1 -I$(SRC)/lib -I$(INC) -Iapple-include -I$(INC)/modules/linux/ -I$(SRC)/kernel -I$(SRC)/drivers -I$(SRC)/modules/linux/libfdt $(PONGO_LDFLAGS) -DDER_TAG_SIZE=8 +PONGO_CC_FLAGS ?= -DPONGO_VERSION='"$(PONGO_VERSION)"' -DAUTOBOOT -DPONGO_PRIVATE=1 -I$(SRC)/lib -I$(INC) -Iapple-include -I$(INC)/modules/linux/ -I$(SRC)/kernel -I$(SRC)/drivers -I$(SRC)/modules/linux/libfdt $(PONGO_LDFLAGS) -DDER_TAG_SIZE=8 -mcpu=cortex-a76 # KPF options CHECKRA1N_LDFLAGS ?= -Wl,-kext @@ -88,7 +88,10 @@ CHECKRA1N_CC ?= $(EMBEDDED_CC) .PHONY: all always clean distclean -all: $(BUILD)/Pongo.bin | $(BUILD) +all: $(BUILD)/Pongo.bin $(BUILD)/pongoOS.AppleSi.macho | $(BUILD) + +$(BUILD)/pongoOS.AppleSi.macho: $(BUILD)/Pongo.bin $(BUILD)/machopack | $(BUILD) + $(BUILD)/machopack $(BUILD)/Pongo.bin $@ $(BUILD)/PongoConsolidated.bin: $(BUILD)/Pongo.bin $(BUILD)/checkra1n-kpf-pongo | $(BUILD) bash -c "echo 6175746F626F6F740000200000000000 | xxd -ps -r | cat $(BUILD)/Pongo.bin <(dd if=/dev/zero bs=1 count="$$(((8 - ($$($(STAT) $(BUILD)/Pongo.bin) % 8)) % 8))") /dev/stdin $(BUILD)/checkra1n-kpf-pongo > $@" @@ -107,6 +110,9 @@ $(BUILD)/checkra1n-kpf-pongo: $(CHECKRA1N_C) $(LIB)/lib/libc.a | $(BUILD) $(BUILD)/vmacho: $(AUX)/vmacho.c | $(BUILD) $(CC) -Wall -O3 -o $@ $^ $(CFLAGS) +$(BUILD)/machopack: $(AUX)/machopack.c | $(BUILD) + $(CC) -Wall -O3 -o $@ $^ $(CFLAGS) + $(BUILD): mkdir -p $@ diff --git a/linuxloader-crlm/dtree-dict.c b/linuxloader-crlm/dtree-dict.c new file mode 100644 index 00000000..ee184611 --- /dev/null +++ b/linuxloader-crlm/dtree-dict.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * + * Copyright (C) 2017-21 Corellium LLC + * All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include "dtree.h" + +typedef struct dt_elem_s { + unsigned depth; + struct dt_elem_s *left, *right, *parent, **up; + char *str; +} dt_elem; + +struct dt_dict_s { + int size; + dt_elem *root; +}; + +dt_dict *dt_dict_new(int size) +{ + dt_dict *dict = calloc(1, sizeof(dt_dict)); + if(!dict) + return dict; + dict->size = size; + return dict; +} + +static int dt_dict_delta(dt_elem *elem) +{ + int res = 0; + if(elem->left) + res -= elem->left->depth + 1; + if(elem->right) + res += elem->right->depth + 1; + return res; +} + +static void dt_dict_update(dt_elem *elem) +{ + elem->depth = 0; + if(elem->left) + elem->depth = elem->left->depth + 1; + if(elem->right && elem->depth < elem->right->depth + 1) + elem->depth = elem->right->depth + 1; +} + +static void dt_dict_pivot(dt_elem *root, dt_elem *pivot) +{ + pivot->parent = root->parent; + pivot->up = root->up; + *(pivot->up) = pivot; + + if(pivot == root->left) { + root->left = pivot->right; + if(root->left) { + root->left->parent = root; + root->left->up = &(root->left); + } + + root->parent = pivot; + root->up = &(pivot->right); + pivot->right = root; + } else { + root->right = pivot->left; + if(root->right) { + root->right->parent = root; + root->right->up = &(root->right); + } + + root->parent = pivot; + root->up = &(pivot->left); + pivot->left = root; + } + + dt_dict_update(root); + dt_dict_update(pivot); +} + +static void dt_dict_rebalance(dt_elem *elem) +{ + int delta; + + dt_dict_update(elem); + + for(; elem; elem=elem->parent) { + delta = dt_dict_delta(elem); + if(delta < -1 || delta > 1) { + if(delta < -2 || delta > 2) + return; + + if(delta < -1) { + if(dt_dict_delta(elem->left) > 0) + dt_dict_pivot(elem->left, elem->left->right); + dt_dict_pivot(elem, elem->left); + } else { + if(dt_dict_delta(elem->right) < 0) + dt_dict_pivot(elem->right, elem->right->left); + dt_dict_pivot(elem, elem->right); + } + elem = elem->parent; + } + + if(!elem || !elem->parent) + break; + + dt_dict_update(elem->parent); + } +} + +void *dt_dict_find(dt_dict *_dict, char *str, unsigned mode) +{ + dt_dict *dict = _dict; + dt_elem **pelem = &(dict->root), *parent = NULL; + int cmp; + + while(*pelem) { + cmp = strcmp(str, (*pelem)->str); + if(!cmp) { + if(!(mode & DT_DICT_FIND)) + return NULL; + return (*pelem) + 1; + } + + parent = *pelem; + if(cmp < 0) + pelem = &(*pelem)->left; + else + pelem = &(*pelem)->right; + } + + if(!(mode & DT_DICT_ADD)) + return NULL; + + (*pelem) = calloc(1, sizeof(dt_elem) + dict->size); + if(!*pelem) + return NULL; + + (*pelem)->str = strdup(str); + (*pelem)->up = pelem; + (*pelem)->parent = parent; + + parent = *pelem; + dt_dict_rebalance(*pelem); + + return parent + 1; +} + +static void dt_dict_iter_recurse(dt_elem *elem, void(*func)(void *param, char *str, void *elem), void *param) +{ + if(elem->left) + dt_dict_iter_recurse(elem->left, func, param); + func(param, elem->str, elem + 1); + if(elem->right) + dt_dict_iter_recurse(elem->right, func, param); +} + +void dt_dict_iter(dt_dict *_dict, void(*func)(void *param, char *str, void *elem), void *param) +{ + dt_dict *dict = _dict; + if(dict->root) + dt_dict_iter_recurse(dict->root, func, param); +} + +char *dt_dict_str(void *_elem) +{ + dt_elem *elem = _elem; + return elem[-1].str; +} + +static void dt_dict_free_recurse(dt_elem *elem) +{ + if(elem->left) + dt_dict_free_recurse(elem->left); + if(elem->right) + dt_dict_free_recurse(elem->right); + free(elem->str); + free(elem); +} + +void dt_dict_free(dt_dict *_dict) +{ + dt_dict *dict = _dict; + if(dict->root) + dt_dict_free_recurse(dict->root); + free(dict); +} diff --git a/linuxloader-crlm/dtree.c b/linuxloader-crlm/dtree.c new file mode 100644 index 00000000..8aaa57cc --- /dev/null +++ b/linuxloader-crlm/dtree.c @@ -0,0 +1,646 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * + * Copyright (C) 2017-21 Corellium LLC + * All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include "dtree.h" + +#define OF_DT_HEADER 0xD00DFEED +#define OF_DT_BEGIN_NODE 0x1 +#define OF_DT_END_NODE 0x2 +#define OF_DT_PROP 0x3 +#define OF_DT_NOP 0x4 +#define OF_DT_END 0x9 + +typedef struct dt_name_s { + unsigned offset, refcount; +} dt_name; + +dtree *dt_new(void) +{ + dtree *tree = calloc(1, sizeof(dtree)); + tree->names = dt_dict_new(sizeof(dt_name)); + if(!tree->names) { + free(tree); + return NULL; + } + return tree; +} + +unsigned dt_get32be(void *ptr) +{ + unsigned char *buf = ptr; + return ((unsigned)buf[0] << 24) | ((unsigned)buf[1] << 16) | ((unsigned)buf[2] << 8) | buf[3]; +} + +void dt_put32be(void *ptr, unsigned val) +{ + unsigned char *buf = ptr; + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; +} + +unsigned long dt_get64be(void *ptr) +{ + unsigned char *buf = ptr; + return ((unsigned long)buf[0] << 56) | ((unsigned long)buf[1] << 48) | ((unsigned long)buf[2] << 40) | ((unsigned long)buf[3] << 32) | + ((unsigned long)buf[4] << 24) | ((unsigned long)buf[5] << 16) | ((unsigned long)buf[6] << 8) | buf[7]; +} + +void dt_put64be(void *ptr, unsigned long val) +{ + unsigned char *buf = ptr; + buf[0] = val >> 56; + buf[1] = val >> 48; + buf[2] = val >> 40; + buf[3] = val >> 32; + buf[4] = val >> 24; + buf[5] = val >> 16; + buf[6] = val >> 8; + buf[7] = val; +} + +static int dt_parse_dtb_node(unsigned char *data, unsigned datal, unsigned char *str, unsigned strl, dt_node **pnode, dt_node *parent, dt_dict *names) +{ + unsigned ptr, tkn, nptr; + int res; + dt_node **pchild; + dt_prop **pprop; + dt_name *pname; + + while(1) { + if(datal < 8) { + printf("[dtree] Invalid device tree binary (%d bytes left in node).\n", datal); + return -1; + } + if(dt_get32be(data) == OF_DT_NOP) { + data += 4; + datal -= 4; + continue; + } + if(dt_get32be(data) != OF_DT_BEGIN_NODE) { + printf("[dtree] Invalid device tree binary (start node token: 0x%08X).\n", dt_get32be(data)); + return -1; + } + break; + } + + ptr = 4; + while(data[ptr]) { + if(ptr >= datal - 4) { + printf("[dtree] Invalid device tree binary (unterminated node name).\n"); + return -1; + } + ptr ++; + } + ptr ++; + + *pnode = calloc(1, sizeof(dt_node)); + if(!*pnode) { + printf("[dtree] Failed to allocate node.\n"); + return -1; + } + (*pnode)->name = strdup((char *)data + 4); + (*pnode)->parent = parent; + ptr = (ptr + 3) & ~3; + pchild = &(*pnode)->child; + pprop = &(*pnode)->prop; + + while(1) { + if(datal - ptr < 4) { + printf("[dtree] Invalid device tree binary (%d bytes left in node).\n", datal - ptr); + return -1; + } + tkn = dt_get32be(data + ptr); + if(tkn == OF_DT_BEGIN_NODE) { + res = dt_parse_dtb_node(data + ptr, datal - ptr, str, strl, pchild, *pnode, names); + if(res < 0) + return res; + ptr += res; + pchild = &(*pchild)->next; + } else if(tkn == OF_DT_END_NODE) { + ptr += 4; + break; + } else if(tkn == OF_DT_PROP) { + if(datal - ptr < 12) { + printf("[dtree] Invalid device tree binary (%d bytes left in property).\n", datal - ptr); + return -1; + } + *pprop = calloc(1, sizeof(dt_prop)); + if(!*pprop) { + printf("[dtree] Failed to allocate property.\n"); + return -1; + } + (*pprop)->parent = *pnode; + (*pprop)->size = dt_get32be(data + ptr + 4); + nptr = dt_get32be(data + ptr + 8); + if(nptr >= strl || !memchr(str + nptr, 0, strl - nptr)) { + printf("[dtree] Invalid device tree binary (property name outside string block).\n"); + return -1; + } + pname = dt_dict_find(names, (char *)str + nptr, DT_DICT_ANY); + if(!pname) { + printf("[dtree] Failed to find property name '%s' in dictionary.\n", str + nptr); + return -1; + } + pname->refcount ++; + (*pprop)->name = pname; + if((*pprop)->size) { + (*pprop)->buf = malloc((*pprop)->size); + if(!(*pprop)->buf) { + printf("[dtree] Failed to allocate property data of size %d.\n", (*pprop)->size); + return -1; + } + memcpy((*pprop)->buf, data + ptr + 12, (*pprop)->size); + } + ptr += ((*pprop)->size + 15) & ~3; + pprop = &(*pprop)->next; + } else if(tkn == OF_DT_NOP) { + ptr += 4; + } else { + printf("[dtree] Invalid device tree binary (node token: 0x%08X).\n", tkn); + return -1; + } + } + + return ptr; +} + +dtree *dt_parse_dtb(void *_dtb, unsigned dtblen) +{ + unsigned char *dtb = _dtb; + dtree *tree; + unsigned totalsize, off_struct, off_string, off_memrsv, len_struct, len_string, len_memrsv; + unsigned i; + + if(dt_get32be(dtb) != OF_DT_HEADER) { + printf("[dtree] Invalid device tree binary (signature: 0x%08X).\n", dt_get32be(dtb)); + return NULL; + } + + totalsize = dt_get32be(dtb + 4); + off_struct = dt_get32be(dtb + 8); + off_string = dt_get32be(dtb + 12); + off_memrsv = dt_get32be(dtb + 16); + + if(totalsize > dtblen) { + printf("[dtree] Invalid device tree binary (length %d exceeds buffer size %d).\n", totalsize, dtblen); + return NULL; + } + + tree = dt_new(); + if(!tree) + return NULL; + + len_memrsv = totalsize - off_memrsv; + if(off_string > off_memrsv && len_memrsv > off_string - off_memrsv) + len_memrsv = off_string - off_memrsv; + if(off_struct > off_memrsv && len_memrsv > off_struct - off_memrsv) + len_memrsv = off_struct - off_memrsv; + + len_struct = totalsize - off_struct; + if(off_string > off_struct && len_struct > off_string - off_struct) + len_struct = off_string - off_struct; + if(off_memrsv > off_struct && len_struct > off_memrsv - off_struct) + len_struct = off_memrsv - off_struct; + + len_string = totalsize - off_string; + if(off_struct > off_string && len_string > off_struct - off_string) + len_string = off_struct - off_string; + if(off_memrsv > off_string && len_string > off_memrsv - off_string) + len_string = off_memrsv - off_string; + + tree->bootcpuid = dt_get32be(dtb + 28); + + len_memrsv >>= 4; + for(i=0; inmemrsv = i; + if(tree->nmemrsv) { + tree->memrsv = calloc(sizeof(struct dt_memrsv_s), tree->nmemrsv); + if(!tree->memrsv) { + dt_free(tree); + return NULL; + } + for(i=0; inmemrsv; i++) { + tree->memrsv[i].base = dt_get64be(dtb + off_memrsv + i * 16); + tree->memrsv[i].size = dt_get64be(dtb + off_memrsv + i * 16 + 8); + } + } + + if(dt_parse_dtb_node(dtb + off_struct, len_struct, dtb + off_string, len_string, &tree->root, NULL, tree->names) < 0) { + dt_free(tree); + return NULL; + } + return tree; +} + +static void dt_write_dtb_offset_func(void *_param, char *str, void *_elem) +{ + unsigned *param = _param; + dt_name *elem = _elem; + + if(!elem->refcount) { + elem->offset = 0xFFFFFFFF; + return; + } + elem->offset = *param; + *param += strlen(dt_dict_str(elem)) + 1; +} + +static void dt_write_dtb_write_func(void *_param, char *str, void *_elem) +{ + char *param = _param; + dt_name *elem = _elem; + + if(elem->offset == 0xFFFFFFFF) + return; + strcpy(param + elem->offset, dt_dict_str(elem)); +} + +static int dt_write_dtb_node(dt_node *node, unsigned char *dtb, unsigned dtbmaxlen) +{ + dt_name *name; + dt_prop *prop; + dt_node *child; + unsigned offset; + int res; + + if(!node) + return 0; + + offset = 4 + ((strlen(node->name) + 4) & ~3); + if(dtbmaxlen < offset + 4) + return -1; + + dt_put32be(dtb, OF_DT_BEGIN_NODE); + strcpy((char *) dtb + 4, node->name); + + for(prop=node->prop; prop; prop=prop->next) { + if(((prop->size + 15) & ~3) > dtbmaxlen - offset) + return -1; + name = prop->name; + if(name->offset == 0xFFFFFFFF) + return -1; + dt_put32be(dtb + offset, OF_DT_PROP); + dt_put32be(dtb + offset + 4, prop->size); + dt_put32be(dtb + offset + 8, name->offset); + if(prop->size) + memcpy(dtb + offset + 12, prop->buf, prop->size); + offset += (prop->size + 15) & ~3; + } + + for(child=node->child; child; child=child->next) { + res = dt_write_dtb_node(child, dtb + offset, dtbmaxlen - offset); + if(res < 0) + return res; + offset += (res + 3) & ~3; + } + + if(dtbmaxlen - offset < 4) + return -1; + dt_put32be(dtb + offset, OF_DT_END_NODE); + + return offset + 4; +} + +unsigned dt_write_dtb(dtree *tree, void *_dtb, unsigned dtbmaxlen) +{ + unsigned char *dtb = _dtb; + unsigned len_string = 0; + int len_struct, len_memrsv, i; + + dt_dict_iter(tree->names, dt_write_dtb_offset_func, &len_string); + len_string = (len_string + 3) & ~3; + + len_memrsv = (tree->nmemrsv + 1) * 16; + + if(dtbmaxlen < 44 + len_memrsv + len_string) + return 0; + + len_struct = dt_write_dtb_node(tree->root, dtb + 40 + len_memrsv, dtbmaxlen - len_string - 44 - len_memrsv); + if(len_struct < 0) + return -1; + dt_put32be(dtb + 40 + len_memrsv + len_struct, OF_DT_END); + len_struct += 4; + + dt_dict_iter(tree->names, dt_write_dtb_write_func, dtb + 40 + len_memrsv + len_struct); + + for(i=0; inmemrsv; i++) { + dt_put64be(dtb + 40 + 16 * i, tree->memrsv[i].base); + dt_put64be(dtb + 48 + 16 * i, tree->memrsv[i].size); + } + memset(dtb + 40 + 16 * tree->nmemrsv, 0, 16); + + dt_put32be(dtb, OF_DT_HEADER); + dt_put32be(dtb + 4, 40 + len_memrsv + len_struct + len_string); + dt_put32be(dtb + 8, 40 + len_memrsv); + dt_put32be(dtb + 12, 40 + len_memrsv + len_struct); + dt_put32be(dtb + 16, 40); + dt_put32be(dtb + 20, 17); /* version */ + dt_put32be(dtb + 24, 16); /* last compatible version */ + dt_put32be(dtb + 28, tree->bootcpuid); + dt_put32be(dtb + 32, len_string); + dt_put32be(dtb + 36, len_struct); + + return 40 + len_memrsv + len_struct + len_string; +} + +static int dt_is_string(char *str, unsigned len) +{ + unsigned cnt; + if(len < 2) + return 0; + if(str[len - 1]) + return 0; + cnt = 0; + while(len) { + if((*str < ' ' || *str > '~') && *str) + return 0; + if(*str) + cnt ++; + else if(!cnt) + return 0; + len --; + str ++; + } + return 1; +} + +dt_node *dt_find_node_idx(dtree *tree, dt_node *start, char *path, int index) +{ + char *sep; + + if(!tree) + return NULL; + + if(!start || (path && path[0] == '/')) + start = tree->root; + if(!start) + return NULL; + + if(!path) { + start = start->child; + while(start) { + if(!index) + return start; + index --; + start = start->next; + } + return NULL; + } + + if(path[0] == '/') + path ++; + + while(*path) { + sep = strchr(path, '/'); + if(!sep) + sep = path + strlen(path); + start = start->child; + while(start) { + if(!strncmp(path, start->name, sep - path) && (!start->name[sep-path] || start->name[sep-path] == '@')) { + if(!*sep) { + if(!index) + break; + index --; + } else + break; + } + start = start->next; + } + if(!start) + return NULL; + path = *sep ? sep + 1 : sep; + } + return start; +} + +dt_prop *dt_find_prop(dtree *tree, dt_node *node, char *name) +{ + void *key; + dt_prop *prop; + if(!tree || !node) + return NULL; + + key = dt_dict_find(tree->names, name, DT_DICT_FIND); + if(!key) + return NULL; + + for(prop=node->prop; prop; prop=prop->next) + if(prop->name == key) + break; + return prop; +} + +dt_node *dt_add_node(dt_node *parent, char *name) +{ + dt_node *node = calloc(1, sizeof(dt_node)), **pnode; + if(!node) + return NULL; + + node->name = strdup(name); + node->parent = parent; + for(pnode=&parent->child; *pnode; pnode=&(*pnode)->next) ; + *pnode = node; + return node; +} + +int dt_delete_node(dt_node *node) +{ + dt_node **pnode; + dt_prop *prop; + if(!node->parent) + return 1; + + for(pnode=&node->parent->child; *pnode; pnode=&(*pnode)->next) + if(*pnode == node) + break; + if(!*pnode) + return 1; + + while(node->child) + if(dt_delete_node(node->child)) + return 1; + + while(node->prop) { + prop = node->prop; + node->prop = prop->next; + ((dt_name *)prop->name)->refcount --; + free(prop->buf); + free(prop); + } + + *pnode = node->next; + free(node); + return 0; +} + +dt_prop *dt_set_prop(dtree *tree, dt_node *node, char *name, void *buf, int size) +{ + dt_name *key = dt_dict_find(tree->names, name, DT_DICT_ANY); + dt_prop **pprop; + void *nbuf; + if(!key) + return NULL; + + if(size < 0 && buf) + size = strlen(buf) + 1; + + for(pprop=&node->prop; *pprop; pprop=&(*pprop)->next) + if((*pprop)->name == key) + break; + if(!*pprop) { + *pprop = calloc(1, sizeof(dt_prop)); + (*pprop)->parent = node; + (*pprop)->name = key; + key->refcount ++; + } + + if((*pprop)->size >= size) { + if(buf) + memcpy((*pprop)->buf, buf, size); + (*pprop)->size = size; + } else { + nbuf = malloc(size); + if(!nbuf) + return NULL; + free((*pprop)->buf); + if(buf) + memcpy(nbuf, buf, size); + (*pprop)->buf = nbuf; + (*pprop)->size = size; + } + + return *pprop; +} + +int dt_delete_prop(dt_prop *prop) +{ + dt_prop **pprop; + + for(pprop=&prop->parent->prop; *pprop; pprop=&(*pprop)->next) + if(*pprop == prop) + break; + if(!*pprop) + return 1; + + ((dt_name *)prop->name)->refcount --; + free(prop->buf); + + *pprop = prop->next; + free(prop); + return 0; +} + +dt_node *dt_copy_node(dtree *tree, dt_node *parent, dt_node *source) +{ + dt_node **pnode, *iter; + dt_prop *prop; + + if(!parent || !source) + return NULL; + + for(pnode=&parent->child; *pnode; pnode=&(*pnode)->next) + if(!strcmp((*pnode)->name, source->name)) + break; + if(!*pnode) { + *pnode = calloc(1, sizeof(dt_node)); + if(!*pnode) + return NULL; + (*pnode)->name = strdup(source->name); + (*pnode)->parent = parent; + } + + for(iter=source->child; iter; iter=iter->next) + if(!dt_copy_node(tree, *pnode, iter)) + return NULL; + + for(prop=source->prop; prop; prop=prop->next) + if(!dt_set_prop(tree, *pnode, dt_dict_str(prop->name), prop->buf, prop->size)) + return NULL; + + return *pnode; +} + +static void dt_dump_node(dt_node *node, unsigned indent) +{ + dt_node *child; + dt_prop *prop; + unsigned i, v; + + printf("%*s%s {\n", indent, "", node->name[0] ? node->name : "/"); + for(prop=node->prop; prop; prop=prop->next) { + printf("%*s%s", indent + 2, "", dt_dict_str(prop->name)); + if(prop->size) { + printf(" = "); + if(!dt_is_string(prop->buf, prop->size)) { + if(prop->size & 3) { + printf("["); + for(i=0; isize; i++) + printf(" %02x", ((unsigned char *)prop->buf)[i]); + printf(" ]"); + } else { + for(i=0; isize/4; i++) { + v = dt_get32be(((unsigned char *)prop->buf) + i * 4); + printf(v >= 16 ? "%s0x%x" : "%s%d", i ? ", " : "", v); + } + } + } else + for(i=0; isize; i+=strlen(prop->buf+i)+1) + printf("%s\"%s\"", i ? ", " : "", (char *) prop->buf + i); + } + printf("\n"); + } + for(child=node->child; child; child=child->next) + dt_dump_node(child, indent + 2); + printf("%*s}\n", indent, ""); +} + +void dt_dump(dtree *tree) +{ + unsigned i; + for(i=0; inmemrsv; i++) + printf("/memreserve/ 0x%lx 0x%lx;\n", tree->memrsv[i].base, tree->memrsv[i].size); + dt_dump_node(tree->root, 0); +} + +static void dt_free_node(dt_node *node) +{ + dt_node *child; + dt_prop *prop; + + while(node->child) { + child = node->child; + node->child = child->next; + dt_free_node(child); + } + while(node->prop) { + prop = node->prop; + node->prop = prop->next; + free(prop->buf); + free(prop); + } + + free(node->name); + free(node); +} + +void dt_free(dtree *tree) +{ + dt_free_node(tree->root); + dt_dict_free(tree->names); + free(tree); +} diff --git a/linuxloader-crlm/dtree.h b/linuxloader-crlm/dtree.h new file mode 100644 index 00000000..0b34a760 --- /dev/null +++ b/linuxloader-crlm/dtree.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * + * Copyright (C) 2017-21 Corellium LLC + * All rights reserved. + * + */ + +#ifndef _DTREE_H +#define _DTREE_H + +typedef struct dt_dict_s dt_dict; + +#define DT_DICT_ADD 1 +#define DT_DICT_FIND 2 +#define DT_DICT_ANY 3 + +dt_dict *dt_dict_new(int size); +void *dt_dict_find(dt_dict *dict, char *str, unsigned mode); +void dt_dict_iter(dt_dict *dict, void(*func)(void *param, char *str, void *elem), void *param); +char *dt_dict_str(void *elem); +void dt_dict_free(dt_dict *dict); + +typedef struct dt_prop_s { + void *name; + void *buf; + unsigned size; + struct dt_prop_s *next; + struct dt_node_s *parent; +} dt_prop; + +typedef struct dt_node_s { + dt_prop *prop; + char *name; + struct dt_node_s *child, *next, *parent; +} dt_node; + +typedef struct dtree_s { + dt_dict *names; + dt_node *root; + struct dt_memrsv_s { + unsigned long base, size; + } *memrsv; + unsigned nmemrsv; + unsigned bootcpuid; +} dtree; + +dtree *dt_new(void); +dtree *dt_parse_dtb(void *dtb, unsigned dtblen); +unsigned dt_write_dtb(dtree *tree, void *dtb, unsigned dtbmaxlen); +dt_node *dt_find_node_idx(dtree *tree, dt_node *start, char *path, int index); +static inline dt_node *dt_find_node(dtree *tree, char *path) { return dt_find_node_idx(tree, (void *)0, path, 0); } +dt_prop *dt_find_prop(dtree *tree, dt_node *node, char *name); +dt_node *dt_add_node(dt_node *parent, char *name); +int dt_delete_node(dt_node *node); +dt_prop *dt_set_prop(dtree *tree, dt_node *node, char *name, void *buf, int size); +int dt_delete_prop(dt_prop *prop); +dt_node *dt_copy_node(dtree *tree, dt_node *parent, dt_node *source); +void dt_dump(dtree *tree); +void dt_free(dtree *tree); + +unsigned dt_get32be(void *ptr); +void dt_put32be(void *ptr, unsigned val); +unsigned long dt_get64be(void *ptr); +void dt_put64be(void *ptr, unsigned long val); + +#endif diff --git a/linuxloader-crlm/main.c b/linuxloader-crlm/main.c new file mode 100644 index 00000000..398ef764 --- /dev/null +++ b/linuxloader-crlm/main.c @@ -0,0 +1,63 @@ + +#import + +#define BOOT_FLAG_JUMP 5 +extern char gBootFlag; +extern uint64_t gBootJumpTo; +extern uint64_t gBootJumpArgs[4]; +extern uint64_t gBootJumpToReloc; +extern uint64_t gBootJumpToRelocSize; +extern uint64_t gBootJumpToRelocFrom; + +extern uint64_t gIORVBAR; +extern uint64_t vatophys_static(void* kva); // only safe to use with phystokva or alloc_contig's return value +extern void* alloc_contig(uint32_t size); + +extern void loader_main(void *linux_dtb, void *bootargs, uint64_t rvbar); + +extern uint8_t __linux_kernel_start[] __asm__("section$start$__LINUX$__kernel"), + __linux_kernel_end[] __asm__("section$end$__LINUX$__kernel"); +extern uint8_t __linux_dtree_start[] __asm__("section$start$__LINUX$__dtree"), + __linux_dtree_end[] __asm__("section$end$__LINUX$__dtree"); + +void module_entry() { + puts("sandcastle linux loader"); + + uint64_t linux_size = ((uint64_t)__linux_kernel_end) - ((uint64_t)__linux_kernel_start); + uint64_t dtree_size = ((uint64_t)__linux_dtree_end) - ((uint64_t)__linux_dtree_start); + + void* rregion = alloc_contig(0x80000 + linux_size); + + void* dtb_copy = rregion + 0x60000; + + memcpy(dtb_copy, __linux_dtree_start, dtree_size); + + loader_main(dtb_copy, (void*)gBootArgs, gIORVBAR); + + puts("... done, booting!"); + sleep(10); + + disable_interrupts(); + + gBootFlag = BOOT_FLAG_JUMP; + + memcpy(rregion + 0x80000, __linux_kernel_start, linux_size); + + gBootJumpToRelocFrom = vatophys_static(rregion); + gBootJumpToReloc = 0x880000000; + gBootJumpToRelocSize = 0x80000 + linux_size; + + gBootJumpArgs[0] = 0x880000000 + 0x60000; + gBootJumpArgs[1] = 0; + gBootJumpArgs[2] = 0; + gBootJumpArgs[3] = 0; + gBootJumpTo = 0x880000000 + 0x80000; + + task_yield_asserted(); +} +char* module_name = "sandcastle-loader"; + +struct pongo_exports exported_symbols[] = { + {.name = 0, .value = 0} +}; + diff --git a/linuxloader-crlm/make-pongo-image.sh b/linuxloader-crlm/make-pongo-image.sh new file mode 100755 index 00000000..b9b2d7e2 --- /dev/null +++ b/linuxloader-crlm/make-pongo-image.sh @@ -0,0 +1 @@ +../build/machopack ../build/Pongo.bin ../pongoOS.AppleSi.macho-module ./test_module diff --git a/linuxloader-crlm/make.sh b/linuxloader-crlm/make.sh new file mode 100755 index 00000000..b1162e53 --- /dev/null +++ b/linuxloader-crlm/make.sh @@ -0,0 +1,24 @@ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# +# Copyright (c) 2019-2020 checkra1n team +# This file is part of pongoOS. +# +cd "$(dirname $0)" +xcrun -sdk iphoneos gcc main.c preboot-c.c -o test_module -arch arm64 -mabi=aapcs -Xlinker -kext -nostdlib -Xlinker -fatal_warnings -I. -D_SECURE__STRING_H_ -O3 dtree.c dtree-dict.c -sectcreate __LINUX __kernel linux.bin -sectcreate __LINUX __dtree hx.dtb diff --git a/linuxloader-crlm/pongo.h b/linuxloader-crlm/pongo.h new file mode 100644 index 00000000..98edd457 --- /dev/null +++ b/linuxloader-crlm/pongo.h @@ -0,0 +1,199 @@ +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// Copyright (c) 2019-2020 checkra1n team +// This file is part of pongoOS. +// + +#include +#include +#include +#include +#include + +#define DT_KEY_LEN 0x20 + +struct Boot_Video { + unsigned long v_baseAddr; /* Base address of video memory */ + unsigned long v_display; /* Display Code (if Applicable */ + unsigned long v_rowBytes; /* Number of bytes per pixel row */ + unsigned long v_width; /* Width */ + unsigned long v_height; /* Height */ + unsigned long v_depth; /* Pixel Depth and other parameters */ +}; + +typedef struct boot_args { + uint16_t Revision; /* Revision of boot_args structure */ + uint16_t Version; /* Version of boot_args structure */ + uint64_t virtBase; /* Virtual base of memory */ + uint64_t physBase; /* Physical base of memory */ + uint64_t memSize; /* Size of memory */ + uint64_t topOfKernelData; /* Highest physical address used in kernel data area */ + struct Boot_Video Video; /* Video Information */ + uint32_t machineType; /* Machine Type */ + void *deviceTreeP; /* Base of flattened device tree */ + uint32_t deviceTreeLength; /* Length of flattened tree */ + char CommandLine[256]; /* Passed in command line */ + uint64_t bootFlags; /* Additional flags specified by the bootloader */ + uint64_t memSizeActual; /* Actual size of memory */ +} boot_args; + +typedef struct +{ + uint32_t nprop; + uint32_t nchld; + char prop[]; +} dt_node_t; + +typedef struct +{ + char key[DT_KEY_LEN]; + uint32_t len; + char val[]; +} dt_prop_t; + +typedef struct +{ + const char* name; + dt_node_t* node; +} dt_find_cb_t; + +typedef struct +{ + const char* key; + void* val; + uint32_t len; +} dt_prop_cb_t; +struct memmap { + uint64_t addr; + uint64_t size; +}; + +/* Device Tree manipulation */ + +extern int dt_check(void* mem, uint32_t size, uint32_t* offp); +extern int dt_parse(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, dt_node_t*), void* cbn_arg, int (*cb_prop)(void*, dt_node_t*, int, const char*, void*, uint32_t), void* cbp_arg); +extern dt_node_t* dt_find(dt_node_t* node, const char* name); +extern void* dt_prop(dt_node_t* node, const char* key, uint32_t* lenp); +extern struct memmap* dt_alloc_memmap(dt_node_t* node, const char* name); +extern uint32_t dt_get_u32_prop(const char* device, const char* prop); +extern uint64_t dt_get_u64_prop(const char* device, const char* prop); +extern uint64_t dt_get_u64_prop_i(const char* device, const char* prop, uint32_t idx); + +/* Task management */ + +#define TASK_CAN_CRASH 1 +#define TASK_LINKED 2 +#define TASK_IRQ_HANDLER 4 +#define TASK_PREEMPT 8 +#define TASK_MASK_NEXT_IRQ 16 +#define TASK_HAS_EXITED 32 +#define TASK_WAS_LINKED 64 +#define TASK_HAS_CRASHED 128 + +struct event { + struct task* task_head; +}; + +struct task { + uint64_t x[30]; + uint64_t lr; + uint64_t sp; + uint64_t runcnt; + uint64_t real_lr; + uint64_t fp[18]; + struct task* irq_ret; + uint64_t entry; + void* task_ctx; + uint64_t stack[0x2000 / 8]; + uint64_t irq_count; + uint32_t irq_type; + uint64_t wait_until; + uint32_t sched_count; + uint32_t pid; + struct task* eq_next; + uint64_t anchor[0]; + uint64_t t_flags; + char name[32]; + uint32_t flags; + struct task* next; + struct task* prev; + void (*crash_callback)(); +}; + +extern void task_switch_irq(struct task* to_task); +extern void task_exit_irq(); +extern void task_switch(struct task* to_task); +extern void task_link(struct task* to_task); +extern void task_unlink(struct task* to_task); +extern void task_irq_dispatch(uint32_t intr); +extern void task_yield_asserted(); +extern void task_register_unlinked(struct task* task, void (*entry)()); +extern void register_irq_handler(uint16_t irq_v, struct task* irq_handler); + +/* Core functions */ + +struct pongo_exports { + const char* name; + void * value; +}; + +extern void panic(const char* string); +extern void spin(uint32_t usec); +extern uint64_t get_ticks(); +extern void usleep(uint32_t usec); +extern void sleep(uint32_t sec); +extern volatile uint8_t get_el(void); +extern void cache_invalidate(void *address, size_t size); +extern void cache_clean_and_invalidate(void *address, size_t size); +extern void clock_gate(uint64_t addr, char val); +extern void disable_preemption(); +extern void enable_preemption(); +extern void enable_interrupts(); +extern void disable_interrupts(); + + +/* Shell */ + +extern void command_putc(char val); +extern void command_puts(const char* val); +extern void command_register(const char* name, const char* desc, void (*cb)(const char* cmd, char* args)); +extern char* command_tokenize(char* str, uint32_t strbufsz); +extern void queue_rx_string(char* string); + +/* WDT */ + +extern void wdt_reset(); +extern void wdt_enable(); +extern void wdt_disable(); + +/* Global variables */ + +extern void (*preboot_hook)(); +extern boot_args * gBootArgs; +extern void* gEntryPoint; +extern dt_node_t *gDeviceTree; +extern uint64_t gIOBase; +extern uint64_t gPMGRBase; +extern char* gDevType; +extern void* ramdisk_buf; +extern uint32_t ramdisk_size; +extern uint32_t autoboot_count; +extern uint8_t * loader_xfer_recv_data; +extern uint32_t loader_xfer_recv_count; diff --git a/linuxloader-crlm/preboot-c.c b/linuxloader-crlm/preboot-c.c new file mode 100644 index 00000000..99ce9ecc --- /dev/null +++ b/linuxloader-crlm/preboot-c.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include + +#include "dtree.h" + +#define BOOT_LINE_LENGTH 256 +struct iphone_boot_args { + uint16_t revision; + uint16_t version; + uint64_t virt_base; + uint64_t phys_base; + uint64_t mem_size; + uint64_t top_of_kernel; + struct { + uint64_t phys, display, stride; + uint64_t width, height, depth; + } video; + uint32_t machine_type; + uint64_t dtree_virt; + uint32_t dtree_size; + char cmdline[BOOT_LINE_LENGTH]; +}; + +void loader_main(void *linux_dtb, struct iphone_boot_args *bootargs, uint64_t rvbar) +{ + dtree *linux_dt; + dt_node *node; + dt_prop *prop; + uint64_t memsize, base; + unsigned i; + + memsize = (bootargs->mem_size + 0x3ffffffful) & ~0x3ffffffful; + + printf("Starting Linux loader stub.\n"); + + linux_dt = dt_parse_dtb(linux_dtb, 0x20000); + if(!linux_dt) { + printf("Failed parsing Linux device-tree.\n"); + return; + } + + node = dt_find_node(linux_dt, "/framebuffer"); + if(node) { + prop = dt_find_prop(linux_dt, node, "reg"); + if(prop) { + dt_put64be(prop->buf, bootargs->video.phys); + dt_put64be(prop->buf + 8, (bootargs->video.height * bootargs->video.stride * 4 + 0x3FFFul) & ~0x3FFFul); + } + + prop = dt_find_prop(linux_dt, node, "width"); + if(prop) + dt_put32be(prop->buf, bootargs->video.width); + + prop = dt_find_prop(linux_dt, node, "height"); + if(prop) + dt_put32be(prop->buf, bootargs->video.height); + + prop = dt_find_prop(linux_dt, node, "stride"); + if(prop) + dt_put32be(prop->buf, bootargs->video.stride); + } + + node = dt_find_node(linux_dt, "/memory"); + if(node) { + prop = dt_find_prop(linux_dt, node, "reg"); + if(prop) + dt_put64be(prop->buf + 8, memsize); + } + + for(i=0; ; i++) { + node = dt_find_node_idx(linux_dt, NULL, "/reserved-memory/fw_area", i); + if(!node) + break; + + prop = dt_find_prop(linux_dt, node, "reg"); + if(prop) { + base = dt_get64be(prop->buf); + if(base >= 0x900000000ul) { + base += memsize - 0x200000000ul; + dt_put64be(prop->buf, base); + } + } + } + + node = dt_find_node(linux_dt, "/reserved-memory/smpentry"); + if(node) { + prop = dt_find_prop(linux_dt, node, "reg"); + if(prop) + dt_put64be(prop->buf, rvbar & ~0xfff); + } + + node = dt_find_node(linux_dt, "/soc/applestart"); + if(node) { + prop = dt_find_prop(linux_dt, node, "reg"); + if(prop) + for(i=0; isize/48; i++) { + printf("RVBAR(%d) = %llx\n", i, rvbar + 8 * i); + dt_put64be(prop->buf + 48 * i + 16, rvbar + 8 * i); + } + } + + printf("Loader complete, relocating kernel...\n"); + dt_write_dtb(linux_dt, linux_dtb, 0x20000); + dt_free(linux_dt); +} diff --git a/src/boot/entry.S b/src/boot/entry.S index 99e2caca..7c72b1ff 100644 --- a/src/boot/entry.S +++ b/src/boot/entry.S @@ -1,42 +1,337 @@ -/* + /* * pongoOS - https://checkra.in * - * Copyright (C) 2019-2020 checkra1n team + * Copyright (c) 2019-2020 checkra1n team + * Copyright (c) 2017-2021 Corellium LLC * * This file is part of pongoOS. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the checkra1n team or Corellium LLC nor the names + * of their contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + .globl start .align 4 start: -// branch from x27 will end up here mov x9, x8 -// branch from x29 here + b common_start // entry point for raw pongo images + +iorvbar_start: +.align 12 + b iorvbar_core_start // entry point for secondary cores + +.align 8 +macho_start: + b simple_start // entry point for macho pongo images + +.align 8 +iorvbar_core_start: + dsb sy + + mrs x1, mpidr_el1 + lsr x0, x1, #14 + bfxil x0, x1, #0, #2 + and x0, x0, #7 + adr x1, _iorvbar_table +2: dsb sy + ldr x2, [x1, x0, lsl #3] + tbz x2, #0, 1f + and x2, x2, #0xFFFFFFFFFC + + bl _cpuinit + + blr x2 +3: wfe + b 3b +1: wfe + b 2b + +.align 12 + +.globl _iorvbar_table +_iorvbar_table: +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 +.long 0xDEADB070, 0xDEADB070, 0xDEADB070, 0xDEADB070 + + +#define SR_H13_MIGSTS s3_4_c15_c0_4 +#define SR_H13_HID0 s3_0_c15_c0_0 +#define SR_H13_HID1 s3_0_c15_c1_0 +#define SR_H13_HID3 s3_0_c15_c3_0 +#define SR_H13_HID4 s3_0_c15_c4_0 +#define SR_H13_EHID4 s3_0_c15_c4_1 +#define SR_H13_HID5 s3_0_c15_c5_0 +#define SR_H13_HID6 s3_0_c15_c6_0 +#define SR_H13_HID7 s3_0_c15_c7_0 +#define SR_H13_HID9 s3_0_c15_c9_0 +#define SR_H13_EHID10 s3_0_c15_c10_1 +#define SR_H13_HID11 s3_0_c15_c11_0 +#define SR_H13_CYC_CFG s3_5_c15_c4_0 +#define SR_H13_CYC_OVRD s3_5_c15_c5_0 +#define SR_H13_LLC_ERR_STS s3_3_c15_c8_0 +#define SR_H13_LLC_ERR_ADR s3_3_c15_c9_0 +#define SR_H13_LLC_ERR_INF s3_3_c15_c10_0 +#define SR_H13_LSU_ERR_STS s3_3_c15_c2_0 +#define SR_H13_LSU_ERR_STS_P s3_3_c15_c0_0 +#define SR_H13_FED_ERR_STS s3_4_c15_c0_2 +#define SR_H13_FED_ERR_STS_P s3_4_c15_c0_0 +#define SR_H13_MMU_ERR_STS s3_6_c15_c2_0 +#define SR_H13_MMU_ERR_STS_P s3_6_c15_c0_0 +#define SR_H13_DPC_ERR_STS s3_5_c15_c0_5 +#define SR_H13_KTRR_LOCK s3_4_c15_c2_2 +#define SR_H13_KTRR_MODE s3_4_c15_c2_5 +#define SR_H13_KTRR_LOWER s3_4_c15_c2_3 +#define SR_H13_KTRR_UPPER s3_4_c15_c2_4 + +.globl _cpuinit +_cpuinit: + msr oslar_el1, xzr + + mrs x18, midr_el1 + and x18, x18, #0xfff0 + cmp x18, #0x0220 + beq cpuinit_h13e + cmp x18, #0x0230 + beq cpuinit_h13p + + ret + +cpuinit_h13e: + msr oslar_el1, xzr + mov x0, #1 + msr s3_6_c15_c1_0, x0 + tlbi vmalle1 + ldr x0, =0x2020a505f020f0f0 + msr s3_6_c15_c1_6, x0 + msr s3_6_c15_c1_0, xzr + tlbi vmalle1 +1: mrs x0, s3_6_c15_c12_4 + tbz x0, #0, 1b + mrs x0, SR_H13_MIGSTS + bic x0, x0, #6 + orr x0, x0, #0x10 + orr x0, x0, #0x1 + msr SR_H13_MIGSTS, x0 + mrs x0, SR_H13_MIGSTS + tbnz x0, #4, 1f + orr x0, x0, #2 + msr SR_H13_MIGSTS, x0 +1: mrs x0, SR_H13_EHID4 + orr x0, x0, #0x800 + orr x0, x0, #0x100000000000 + msr SR_H13_EHID4, x0 + mrs x0, SR_H13_HID5 + orr x0, x0, #0x2000000000000000 + msr SR_H13_HID5, x0 + mrs x0, SR_H13_EHID10 + orr x0, x0, #0x100000000 + orr x0, x0, #0x2000000000000 + msr SR_H13_EHID10, x0 + mrs x0, s3_0_c15_c1_2 + orr x0, x0, #0x100 + msr s3_0_c15_c1_2, x0 + mrs x0, s3_0_c15_c9_1 + bic x0, x0, #0x20 + msr s3_0_c15_c9_1, x0 + mrs x0, s3_0_c15_c1_2 + orr x0, x0, #0x8000 + msr s3_0_c15_c1_2, x0 + mrs x0, s3_0_c15_c1_2 + orr x0, x0, #0x10000 + msr s3_0_c15_c1_2, x0 + mrs x0, s3_0_c15_c1_2 + orr x0, x0, #0x600000 + msr s3_0_c15_c1_2, x0 + mrs x0, mpidr_el1 + and x0, x0, #3 + msr s3_4_c15_c5_0, x0 + mov x0, #0x100 + msr s3_4_c15_c1_4, x0 + mrs x0, SR_H13_CYC_OVRD + bic x0, x0, #0xf00000 + msr SR_H13_CYC_OVRD, x0 + mrs x0, actlr_el1 + orr x0, x0, #0x200 /* something to do with dsb? */ + msr actlr_el1, x0 + mrs x0, SR_H13_CYC_CFG + orr x0, x0, #12 + msr SR_H13_CYC_CFG, x0 + msr SR_H13_LLC_ERR_STS, xzr + + ret + +cpuinit_h13p: + msr oslar_el1, xzr + mov x0, #1 + msr s3_6_c15_c1_0, x0 + tlbi vmalle1 + ldr x0, =0x2020a505f020f0f0 + msr s3_6_c15_c1_6, x0 + msr s3_6_c15_c1_0, xzr + tlbi vmalle1 + mrs x0, s3_0_c15_c14_0 + bic x0, x0, 0xf000000000000000 + orr x0, x0, 0xc000000000000000 + msr s3_0_c15_c14_0, x0 + mrs x0, s3_0_c15_c15_0 + orr x0, x0, 0x100000000 + msr s3_0_c15_c15_0, x0 +1: mrs x0, s3_6_c15_c12_4 + tbz x0, #0, 1b + mrs x0, SR_H13_MIGSTS + bic x0, x0, #6 + orr x0, x0, #0x10 + orr x0, x0, #0x1 + msr SR_H13_MIGSTS, x0 + mrs x0, SR_H13_MIGSTS + tbnz x0, #4, 1f + orr x0, x0, #2 + msr SR_H13_MIGSTS, x0 +1: mrs x0, SR_H13_HID4 + orr x0, x0, #0x800 + orr x0, x0, #0x100000000000 + msr SR_H13_HID4, x0 + mrs x0, SR_H13_HID5 + orr x0, x0, #0x2000000000000000 + msr SR_H13_HID5, x0 + mrs x0, s3_0_c15_c14_0 + bic x0, x0, #0x3c000 + orr x0, x0, #0x10000 + msr s3_0_c15_c14_0, x0 + mrs x0, SR_H13_HID0 + orr x0, x0, #0x200000000000 + msr SR_H13_HID0, x0 + mrs x0, SR_H13_HID3 + bic x0, x0, #0x8000000000000000 + bic x0, x0, #0x100000000000 + msr SR_H13_HID3, x0 + mrs x0, SR_H13_HID1 + orr x0, x0, #0x40000000000000 + msr SR_H13_HID1, x0 + mrs x0, s3_0_c15_c15_2 + orr x0, x0, #0x100000000000000 + orr x0, x0, #0x800000000000000 + orr x0, x0, #0x2000000000000000 + orr x0, x0, #0x4000000000000000 + msr s3_0_c15_c15_2, x0 + mrs x0, SR_H13_HID9 + orr x0, x0, #0x4000000 + msr SR_H13_HID9, x0 + mrs x0, SR_H13_HID4 + orr x0, x0, #0x30000000000 + msr SR_H13_HID4, x0 + mrs x0, SR_H13_HID11 + orr x0, x0, #0x800000000000000 + msr SR_H13_HID11, x0 + mrs x0, SR_H13_HID0 + orr x0, x0, #0x10000000 + orr x0, x0, #0x1000000000 + msr SR_H13_HID0, x0 + mrs x0, SR_H13_HID6 + bic x0, x0, #0x3e0 + msr SR_H13_HID6, x0 + mrs x0, SR_H13_HID7 + orr x0, x0, #0x100000 + orr x0, x0, #0x80000 + orr x0, x0, #0x3000000 + msr SR_H13_HID7, x0 + mrs x0, SR_H13_HID9 + orr x0, x0, #0x1000000000000 + orr x0, x0, #0x20000000 + msr SR_H13_HID9, x0 + mrs x0, s3_0_c15_c11_2 + orr x0, x0, #0x4000 + msr s3_0_c15_c11_2, x0 + mrs x0, s3_0_c15_c1_3 + bic x0, x0, #0x80000 + msr s3_0_c15_c1_3, x0 + mrs x0, SR_H13_HID4 + orr x0, x0, #0x2000000000000 + orr x0, x0, #0x20000000000000 + msr SR_H13_HID4, x0 + mrs x0, SR_H13_HID9 + orr x0, x0, #0x80000000000000 + msr SR_H13_HID9, x0 + mrs x0, SR_H13_HID11 + orr x0, x0, #0x8000 + msr SR_H13_HID11, x0 + mrs x0, SR_H13_HID1 + orr x0, x0, #0x400000000000000 + orr x0, x0, #0x1000000000000000 + msr SR_H13_HID1, x0 + mrs x0, s3_0_c15_c1_3 + orr x0, x0, #0x2000000000000 + msr s3_0_c15_c1_3, x0 + mrs x0, mpidr_el1 + and x0, x0, #3 + msr s3_4_c15_c5_0, x0 + mov x0, #0x100 + msr s3_4_c15_c1_4, x0 + mrs x0, SR_H13_CYC_OVRD + bic x0, x0, #0xf00000 + msr SR_H13_CYC_OVRD, x0 + mrs x0, actlr_el1 + orr x0, x0, #0x200 /* something to do with dsb? */ + msr actlr_el1, x0 + mrs x0, SR_H13_CYC_CFG + orr x0, x0, #12 + msr SR_H13_CYC_CFG, x0 + msr SR_H13_LLC_ERR_STS, xzr + + ret + + + + +.align 14 +common_start: adr x4, start mov x5, #0x800000000 movk x5, #0x1800, lsl#16 - and x30, x30, 0x4 - orr x30, x30, x5 cmp x4, x5 - b.eq start$l0 + b.eq direct_start + + mov x30, x5 add x6, x4, #0x200000 copyloop: @@ -45,34 +340,9 @@ copyloop: cmp x4, x6 b.ne copyloop -#ifdef AUTOBOOT - ldr x3, [x6] - mov x4, #0x800000000 - movk x4, #0x1900, lsl#16 - mov x2, #0x7561 - movk x2, #0x6f74, lsl#16 - movk x2, #0x6f62, lsl#32 - movk x2, #0x746f, lsl#48 - cmp x3, x2 - b.ne nullsub - ldr w2, [x6, #8] - add w2, w2, #16 - and w2, w2, #(~15) - -copyloop_2: - cbz w2, copyloop_3 - sub w2, w2, #16 - ldp x10,x15, [x4], #16 - stp x10,x15, [x5], #16 - b copyloop_2 -#endif -copyloop_3: -#ifdef AUTOBOOT - str xzr, [x6] -#endif ret -start$l0: +direct_start: mov x1, x0 mov x0, x9 mov x29, xzr @@ -81,29 +351,41 @@ start$l0: bl _trampoline_entry b . +simple_start: + mov x29, xzr + bl _set_exception_stack_core0 + bl _set_execution_stack_core0 + bl _simple_entry + b . + + + .globl _setup_el1 _setup_el1: stp x29, x30, [sp, #-0x10]! mov x20, x1 mov x21, x2 mrs x16, currentel + cmp x16, #0x8 + b.eq el2_entry cmp x16, #0x4 b.eq el1_entry cmp x16, #0xc + b.ne . el3_entry: - adr x16, _exception_vector_el3 msr vbar_el3, x16 mov x16, #0x430 msr scr_el3, x16 mov x16, #4 msr spsr_el3, x16 - adr x16, el1_entry msr elr_el3, x0 eret + +el2_entry: el1_entry: blr x0 b . diff --git a/src/boot/stage3.c b/src/boot/stage3.c index 4e11fd5d..200354df 100644 --- a/src/boot/stage3.c +++ b/src/boot/stage3.c @@ -131,11 +131,34 @@ void stage3_exit_to_el1_image(void* boot_args, void* boot_entry_point) { } jump_to_image((uint64_t)gboot_entry_point, (uint64_t)gboot_args); } +extern uint64_t __bss_start[] __asm__("section$start$__DATA$__common"), + __bss_end[] __asm__("segment$end$__DATA"); + +void simple_entry(void* boot_args) { + if (__bss_start[0] == 0x746F6F626F747561) { + uint32_t autoboot_sz = (uint32_t)(__bss_start[1]); + extern volatile void smemcpy128(void*,void*,uint32_t); + smemcpy128 ((void*)0x819000000, __bss_start, (autoboot_sz + 64)/16); + extern volatile void smemset(void*, uint8_t, uint64_t); + smemset(__bss_start, 0, autoboot_sz + 0x20); + } + + gboot_args = boot_args; + gboot_entry_point = NULL; + extern volatile void setup_el1(void * entryp,uint64_t,uint64_t); + + extern volatile void smemset(void*, uint8_t, uint64_t); + smemset(&__bss_start, 0, ((uint64_t)__bss_end) - ((uint64_t)__bss_start)); + + extern void cpuinit(); + cpuinit(); + + extern void main (void); + setup_el1(main, 0, (uint64_t)boot_args); +} void trampoline_entry(void* boot_image, void* boot_args) { - extern uint64_t __bss_start[] __asm__("section$start$__DATA$__common"), - __bss_end[] __asm__("segment$end$__DATA"); if (__bss_start[0] == 0x746F6F626F747561) { uint32_t autoboot_sz = (uint32_t)(__bss_start[1]); extern volatile void smemcpy128(void*,void*,uint32_t); @@ -156,6 +179,7 @@ void trampoline_entry(void* boot_image, void* boot_args) extern volatile void smemset(void*, uint8_t, uint64_t); smemset(&__bss_start, 0, ((uint64_t)__bss_end) - ((uint64_t)__bss_start)); + extern void main (void); setup_el1(main, (uint64_t)boot_image, (uint64_t)boot_args); } diff --git a/src/drivers/aes/aes.c b/src/drivers/aes/aes.c index 371bc375..c38a8193 100644 --- a/src/drivers/aes/aes.c +++ b/src/drivers/aes/aes.c @@ -167,13 +167,16 @@ void aes_init(void) aes_impl = &aes_a9; break; default: - panic("AES: Unsupported SoC"); + puts("AES: Unsupported SoC"); + return; } command_register("aes", "performs AES operations", aes_cmd); } int aes(uint32_t op, const void *src, void *dst, size_t len, const void *iv, const void *key) { + if (!aes_impl) panic("AES: not initialized"); + if((op & ~AES_ALL_MASK) != 0) { return EINVAL; diff --git a/src/drivers/cd3217/cd3217.c b/src/drivers/cd3217/cd3217.c new file mode 100644 index 00000000..87b069dc --- /dev/null +++ b/src/drivers/cd3217/cd3217.c @@ -0,0 +1,281 @@ +#import + +/* + -------------------------------------------------------------------------------------------------------------------------------- + transports-supported 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 |................| + 05 00 00 00 |....| + port-location back-left + compatible usbc,cd3217 + acio-parent 0x00000043 + iicProvider 0x00000093 + AAPL,phandle 0x00000094 + dock 0x000000fd + rid 0x00000000 + hpm-class-type 0x0000000a + hpm-iic-addr 0x00000038 + port-type 0x00000002 + usbc-flash-update 0x00000001 + usbc-fw-personality HPM,27 + port-number 0x00000001 + name hpm0 + -------------------------------------------------------------------------------------------------------------------------------- + + https://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf + + */ + +struct cd3217_ctx { + struct hal_device* bus; + struct hal_device* device; + + lock cd3217_lock; + lock cd3217_reg_lock; + + uint16_t i2c_addr; + struct i2c_cmd* i2ccmd; +}; + +__unused static bool cd3217_reg_read(struct cd3217_ctx* cd, uint8_t regid, void* readout, uint8_t outsz) { + uint8_t buf[64] = {0}; + + lock_take(&cd->cd3217_reg_lock); + + i2c_cmd_set_write_tx(cd->i2ccmd, 0, cd->i2c_addr, ®id, 1); + i2c_cmd_set_read_tx(cd->i2ccmd, 1, cd->i2c_addr, buf, 64); + bool rv = i2c_cmd_perform(cd->bus, cd->i2ccmd); + + if (rv) { + if (buf[0] > outsz) { + buf[0] = outsz; + } + memcpy(readout, &buf[1], buf[0]); + } + + lock_release(&cd->cd3217_reg_lock); +#ifdef CD3217_REG_LOG + iprintf("cd3217_reg_read(%s:%x, reg %x, %x bytes): %d (%d)\n", cd->bus->name, cd->i2c_addr, regid, outsz, rv, buf[0]); +#endif + return rv; +} + +__unused static bool cd3217_reg_write(struct cd3217_ctx* cd, uint8_t regid, const void* readout, uint8_t insz) { + uint8_t buf[64] = {0}; + + lock_take(&cd->cd3217_reg_lock); + + if (insz > 63) { + panic("cd3217: OOB write"); + } + + buf[0] = insz; + memcpy(&buf[1], readout, insz); + + i2c_cmd_set_write_tx(cd->i2ccmd, 0, cd->i2c_addr, ®id, 1); + i2c_cmd_set_write_tx(cd->i2ccmd, 1, cd->i2c_addr, buf, insz + 1); + bool rv = i2c_cmd_perform(cd->bus, cd->i2ccmd); + + lock_release(&cd->cd3217_reg_lock); +#ifdef CD3217_REG_LOG + iprintf("cd3217_reg_write(%s:%x, reg %x, %x bytes): %d\n", cd->bus->name, cd->i2c_addr, regid, insz, rv); +#endif + return rv; +} + + +__unused static bool cd3217_enter_system_power_state(struct cd3217_ctx* cd, uint8_t p_state) { + if (p_state > 5) { + panic("cd3217: invalid pstate requested\n"); + } + + bool rv = cd3217_reg_write(cd, 0x20, &p_state, 1); + if (!rv) { + iprintf("cd3217: couldn't enter power state\n"); + } + + return rv; +} + +__unused static int8_t cd3217_get_system_power_state(struct cd3217_ctx* cd) { + uint8_t p_state = 0; + if (! cd3217_reg_read(cd, 0x20, &p_state, 1)) { + iprintf("cd3217: couldn't get power state\n"); + return -1; + } + + return p_state; +} + +__unused static bool cd3217_issue_cmd(struct cd3217_ctx* cd, const char* cmdname, void* data_in, uint8_t data_in_sz, void* data_out, uint8_t data_out_sz) { + bool status; + lock_take(&cd->cd3217_lock); + + if (data_in_sz) { + status = cd3217_reg_write(cd, 9, data_in, data_in_sz); + if (!status) goto failout; + } + + status = cd3217_reg_write(cd, 8, cmdname, 4); + if (!status) goto failout; + + uint32_t rdcmd = -1; + + while (1) { + status = cd3217_reg_read(cd, 8, &rdcmd, 4); + if (!status || rdcmd == 0x444d4321) goto failout; + if (!rdcmd) { + break; // successfully ran + } + } + + if (data_out_sz) { + status = cd3217_reg_read(cd, 9, data_out, data_out_sz); + if (!status) goto failout; + } + + lock_release(&cd->cd3217_lock); + return true; + +failout: + lock_release(&cd->cd3217_lock); + return false; +} + +static bool cd3217_register(struct hal_device* device, void** context) { + // cd3217 USBC PD controller + + uint32_t len = 0; + dt_node_t* node = device->node; + + uint32_t* val = dt_prop(node, "iicProvider", &len); + if (!val || len != 4) panic("cd3217_register: dt looks broken"); + uint32_t i2c_bus_phandle = *val; + val = dt_prop(node, "hpm-iic-addr", &len); + if (!val || len != 4) panic("cd3217_register: dt looks broken"); + uint32_t i2c_addr = *val; + + struct hal_device* bus = hal_get_phandle_device(i2c_bus_phandle); + if (!bus) panic("cd3217_register: invalid iicProvider"); + bus = bus->parent; + if (!bus) panic("cd3217_register: invalid iicProvider"); + + struct cd3217_ctx* cdctx = calloc(sizeof(struct cd3217_ctx), 1); + + cdctx->bus = bus; + cdctx->device = device; + cdctx->i2ccmd = i2c_cmd_create(2); + cdctx->i2c_addr = i2c_addr; + *context = cdctx; + + return true; +} + +static int cd3217_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + if (method == HAL_METASERVICE_START) { + struct cd3217_ctx* cd = (struct cd3217_ctx*)(svc->context); + + uint32_t vendorID = 0, deviceID = 0; + bool success = false; + success = cd3217_reg_read(cd, 0, &vendorID, 4); + if (!success) panic("cd3217: couldn't fetch VID"); + success = cd3217_reg_read(cd, 1, &deviceID, 4); + if (!success) panic("cd3217: couldn't fetch DID"); + uint32_t cd3217_uid[4]; + success = cd3217_reg_read(cd, 5, cd3217_uid, 16); + if (!success) panic("cd3217: couldn't fetch UID"); + char sts[5]; + bzero(sts, 5); + success = cd3217_reg_read(cd, 3, sts, 4); + if (!success) panic("cd3217: couldn't fetch mode"); + + uint32_t bsts = cd3217_get_system_power_state(cd); + + cd3217_enter_system_power_state(cd, 0); + + if (bsts == 7) { + uint16_t win = 0; + success = cd3217_issue_cmd(cd, "SSPS", &win, 2, NULL, 0); + if (!success) { + iprintf("SSPS fail!\n"); + } else { + iprintf("SSPS success\n"); + } + } + + iprintf("cd3217 device found (%s:%x)! uid: %x, pid = %x, vid = %x, mode = %s, pstate = %d\n", cd->bus->name, cd->i2c_addr, cd3217_uid[0], vendorID, deviceID, sts, bsts); + + + success = cd3217_issue_cmd(cd, "SWDF", NULL, 0, NULL, 0); + if (!success) { + iprintf("SWDF fail!\n"); + } else { + iprintf("SWDF success\n"); + } + + success = cd3217_issue_cmd(cd, "SWsr", NULL, 0, NULL, 0); + if (!success) { + iprintf("SWsr fail!\n"); + } else { + iprintf("SWsr success\n"); + } + + return 0; + } + return -1; +} + +static bool cd3217_probe(struct hal_service* svc, struct hal_device* device, void** context) { + if (hal_device_is_compatible(device, "usbc,cd3217")) { + if (cd3217_register(device, context)) { + return true; + } + } + return false; +} +struct hpm_ctx { + struct task* irq_task; +}; + +static void hpm_irq_main() { + while (1) { + fiprintf(stderr, "hpm irq\n"); + task_exit_irq(); + } +} +static int hpm_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + if (method == HAL_METASERVICE_START) { + struct hpm_ctx* hpm = (struct hpm_ctx*)(svc->context); + if (!hal_register_interrupt(device, hpm->irq_task, 0, hpm)) + panic("hpm_start: hal_register_interrupt failed!"); + + return 0; + } + return -1; +} +static bool hpm_probe(struct hal_service* svc, struct hal_device* device, void** context) { + if (hal_device_is_compatible(device, "usbc,manager")) { + struct hpm_ctx* ctx = calloc(sizeof(struct hpm_ctx), 1); + ctx->irq_task = task_create_extended("hpm", hpm_irq_main, TASK_IRQ_HANDLER | TASK_PREEMPT, 0); + *context = ctx; + return true; + } + return false; +} + +static struct hal_service cd3217_svc = { + .name = "cd3217", + .probe = cd3217_probe, + .service_op = cd3217_service_op +}; +static struct hal_service hpm_svc = { + .name = "hpm", + .probe = hpm_probe, + .service_op = hpm_service_op +}; + +static void cd3217_init(struct driver* driver) { + hal_register_hal_service(&hpm_svc); + hal_register_hal_service(&cd3217_svc); +} + +REGISTER_DRIVER(cd3217, cd3217_init, NULL, 0); + diff --git a/src/drivers/dart/dart.c b/src/drivers/dart/dart.c new file mode 100644 index 00000000..a6f2f224 --- /dev/null +++ b/src/drivers/dart/dart.c @@ -0,0 +1,207 @@ +/* + * pongoOS - https://checkra.in + * + * Copyright (C) 2019-2020 checkra1n team + * + * This file is part of pongoOS. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#import +#import "dart.h" + +#define DART1_ERROR_STATUS 0x0040 +#define DART1_ERROR_FLAG (1 << 30) +#define DART1_ERROR_SID_MASK (15 << 24) +#define DART1_ERROR_APF_REJECT (1 << 11) +#define DART1_ERROR_UNKNOWN (1 << 9) +#define DART1_ERROR_CTRR_WRITE_PROT (1 << 8) +#define DART1_ERROR_REGION_PROT (1 << 7) +#define DART1_ERROR_AXI_SLV_ERR (1 << 6) +#define DART1_ERROR_AXI_SLV_DECODE (1 << 5) +#define DART1_ERROR_READ_PROT (1 << 4) +#define DART1_ERROR_WRITE_PROT (1 << 3) +#define DART1_ERROR_PTE_INVLD (1 << 2) +#define DART1_ERROR_L2E_INVLD (1 << 1) +#define DART1_ERROR_TTBR_INVLD (1 << 0) +#define DART1_ERROR_ADDRESS_LO 0x0050 +#define DART1_ERROR_ADDRESS_HI 0x0054 + +struct t8020_dart { + uint64_t dart_type; + uint64_t dart_regbase; + uint64_t dart_flags; + uint64_t dart_bypass_base; +}; + +__unused static uint32_t dart8020_reg_read(struct t8020_dart* dart, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(dart->dart_regbase + offset); +#ifdef REG_LOG + fiprintf(stderr, "dart8020_reg_read(%x) = %x\n", offset, rv); +#endif + return rv; +} +__unused static void dart8020_reg_write(struct t8020_dart* dart, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "dart8020_reg_write(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(dart->dart_regbase + offset) = value; +} + + +#define DART_FLAGS_MODE_MASK 3 +#define DART_FLAGS_MODE_BYPASS 1 + +struct task* dart_irq_task; + +void dart_irq_handler() { + while (1) { + __unused struct t8020_dart* dart = task_current_interrupt_context(); + + uint32_t dart_error_status = dart8020_reg_read(dart, DART1_ERROR_STATUS); + uint32_t dart_error_addr_lo = dart8020_reg_read(dart, DART1_ERROR_ADDRESS_LO); + uint32_t dart_error_addr_hi = dart8020_reg_read(dart, DART1_ERROR_ADDRESS_HI); + + uint64_t dart_error_addr = dart_error_addr_lo; + dart_error_addr |= ((uint64_t)dart_error_addr_hi) << 32ULL; + + for (int i=0; i < 256; i++) { + fiprintf(stderr, "%02x: %08x ", i*4, dart8020_reg_read(dart, i*4)); + if (3 == (i & 3)) { + fiprintf(stderr, "\n"); + } + } + fiprintf(stderr, "\n"); + panic("DART error! ERRSTS: %x, ERRADDR: %llx", dart_error_status, dart_error_addr); + + + task_exit_irq(); + } +} + +static bool register_dart_mapper(struct hal_device* device, void** context) { + uint32_t len = 0; + dt_node_t* node = device->node; + + if (hal_device_is_compatible(device->parent, "dart,t8020")) { + __unused void* val = dt_prop(node, "name", &len); + + uint32_t* regid = dt_prop(node, "reg", &len); + if (len != 4) regid = NULL; + + uint32_t reg_index = 0; + if (regid) { + reg_index = *regid; + } + + void* regs = hal_map_registers(device->parent, reg_index, NULL); + + if (!regs) { + //iprintf("Couldn't map MMIO for 8020 dart-mapper: %s\n", val); + return false; + } + + struct t8020_dart* dart = calloc(sizeof(struct t8020_dart), 1); + dart->dart_type = 0x8020; + dart->dart_regbase = (uint64_t) regs; + + int dart_irq = hal_get_irqno(device->parent, 0); + if (dart_irq > 0) { + if (!interrupt_context(dart_irq)) { + if (!dart_irq_task) { + dart_irq_task = task_create_extended("dart", dart_irq_handler, TASK_IRQ_HANDLER, 0); // can't be preempt since we map the same task to many different IRQs, which means task_current_interrupt_context is UB + } + task_bind_to_irq(dart_irq_task, dart_irq); + interrupt_associate_context(dart_irq, dart); + } + } + + // iprintf("Found 8020 dart-mapper: %s @ %llx\n", val, dart->dart_regbase); + + *context = dart; + return true; + } + + return false; +} + +static bool dart_probe(struct hal_service* svc, struct hal_device* device, void** context) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (node) { + void* val = dt_prop(node, "device_type", &len); + if (val && strcmp(val, "dart-mapper") == 0) { + return register_dart_mapper(device, context); + } + } + return false; +} +#define DART_TTBR(n) (0x200 + n*4) +#define DART_MODE(n) (0x100 + n*4) +#define DART_MODE_BYPASS(high_byte) ((high_byte << 16) | 0x100) +static int dart_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + struct t8020_dart* dart = ((struct t8020_dart*)svc->context); + if (method == DART_ENTER_BYPASS_MODE) { + if (dart->dart_type == 0x8020) { + for (int i=0; i < 32; i++) { + dart8020_reg_write(dart, DART_MODE(i), DART_MODE_BYPASS(0x8)); + } + for (int i=0; i < 32; i++) { + dart8020_reg_write(dart, DART_TTBR(i), 0); + } + + dart->dart_bypass_base = 0x800000000; + + dart->dart_flags &= ~DART_FLAGS_MODE_MASK; + dart->dart_flags |= DART_FLAGS_MODE_BYPASS; + + return dart_service_op(svc, device, DART_FLUSH_CACHE, NULL, 0, NULL, 0); + } + } else if (method == DART_FLUSH_CACHE) { + if (dart->dart_type == 0x8020) { + dart8020_reg_write(dart, 0x34, 0); + dart8020_reg_write(dart, 0x20, 0); + while(dart8020_reg_read(dart, 0x20) & 4) {} + return 0; + } + } else if (method == DART_BYPASS_CONVERT_PTR && data_in_size == 8 && data_out_size && *data_out_size == 8) { + if ((dart->dart_flags & DART_FLAGS_MODE_MASK) == DART_FLAGS_MODE_BYPASS) { + uint64_t inptr = *(uint64_t*)data_in; + + if (inptr >= dart->dart_bypass_base) { + *(uint64_t*)data_out = inptr - dart->dart_bypass_base; + return 0; + } + } + } + return -1; +} + +static struct hal_service dart_svc = { + .name = "dart", + .probe = dart_probe, + .service_op = dart_service_op +}; + +static void dart_init(struct driver* driver) { + hal_register_hal_service(&dart_svc); +} + +REGISTER_DRIVER(dart, dart_init, NULL, 0); diff --git a/src/drivers/dart/dart.h b/src/drivers/dart/dart.h new file mode 100644 index 00000000..9c214cd8 --- /dev/null +++ b/src/drivers/dart/dart.h @@ -0,0 +1,3 @@ +#define DART_ENTER_BYPASS_MODE 0 +#define DART_FLUSH_CACHE 1 +#define DART_BYPASS_CONVERT_PTR 2 diff --git a/src/drivers/framebuffer/fb.c b/src/drivers/framebuffer/fb.c index 1cf86b9f..c8e4d9a1 100644 --- a/src/drivers/framebuffer/fb.c +++ b/src/drivers/framebuffer/fb.c @@ -125,7 +125,10 @@ uint32_t colors_mix_alpha(uint32_t color1, uint32_t color2) { return color_compose_v32(componentsw); } - +void fb_reset_cursor() { + y_cursor = bannerHeight; + x_cursor = LEFT_MARGIN; +} void screen_putc(uint8_t c) { if (!gFramebuffer) return; diff --git a/src/drivers/gpio/gpio.c b/src/drivers/gpio/gpio.c index 67d8fdb9..0ad9cdb7 100644 --- a/src/drivers/gpio/gpio.c +++ b/src/drivers/gpio/gpio.c @@ -26,29 +26,95 @@ */ #include -void gpio_main() { - while(1) { - iprintf("gpio irq %x\n", task_current()->irq_type); +/* + #interrupt-cells 0x00000002 + interrupt-controller || + compatible gpio,t8101 + interrupt-parent 0x0000005c + interrupts be 00 00 00 bf 00 00 00 c0 00 00 00 c1 00 00 00 |................| + c2 00 00 00 c3 00 00 00 c4 00 00 00 |............| + #gpio-int-groups 0x00000007 + reg 00 00 10 3c 00 00 00 00 00 00 10 00 00 00 00 00 |...<............| + #gpio-pins 0x000000d4 + AAPL,phandle 0x00000063 + device_type interrupt-controller + #address-cells 0x00000000 + role AP + name gpio + + + */ + + +struct t8101_gpio_ctx { + struct task* irq_task; + uint64_t gpio_base; +}; + +static int gpio_irqctrl_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + if (method == IRQ_MASK && data_in_size == 4) { + __unused uint32_t irq_index = *(uint32_t*)data_in; + } else if (method == IRQ_UNMASK && data_in_size == 4) { + __unused uint32_t irq_index = *(uint32_t*)data_in; + } else if (method == IRQ_ACK) { + __unused uint32_t irq_index = *(uint32_t*)data_in; + } else if (method == IRQ_REGISTER && data_in_size == sizeof(struct irq_register_args)) { + __unused struct irq_register_args* args = data_in; + return 0; + } + return -1; +} + +static struct hal_service gpio_irqctrl_svc = { + .name = "irqctrl", + .probe = NULL, + .service_op = gpio_irqctrl_service_op +}; + +static void gpio_t8101_irq() { + while (1) { + fiprintf(stderr, "GPIO IRQ\n"); task_exit_irq(); } } -struct task gpio_task = {.name = "gpio"}; -uint64_t gGpioBase; -void gpio_early_init() { - gGpioBase = dt_get_u32_prop("gpio", "reg"); - gGpioBase += gIOBase; +static bool gpio_register_t8101(struct hal_device* device, void** context) { + struct t8101_gpio_ctx* gpioctx = calloc(sizeof(struct t8101_gpio_ctx), 1); + gpioctx->gpio_base = (uint64_t) hal_map_registers(device, 0, NULL); + gpioctx->irq_task = task_create_extended(device->name, gpio_t8101_irq, TASK_IRQ_HANDLER, 0); + hal_associate_service(device, &gpio_irqctrl_svc, gpioctx); + *context = gpioctx; + return true; +} + + +static bool gpio_t8101_probe(struct hal_service* svc, struct hal_device* device, void** context) { + if (hal_device_is_compatible(device, "gpio,t8101")) { + return gpio_register_t8101(device, context); + } + return false; +} + +static int gpio_t8101_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + if (method == HAL_METASERVICE_START) { + struct t8101_gpio_ctx* gpioctx = svc->context; + if (!hal_register_interrupt(device, gpioctx->irq_task, 0, gpioctx)) + panic("gpio_t8101_start: hal_register_interrupt failed!"); + + return 0; + } + return -1; } -void gpio_init() { - /* - uint32_t len = 0; - dt_node_t* buttons = dt_find(gDeviceTree, "buttons"); - if (!buttons) panic("invalid devicetree: no buttons!"); - uint32_t* interrupts = dt_prop(buttons, "interrupts", &len); - if (!interrupts) panic("invalid devicetree: no interrupts!"); - - for (int i=0; inext = parentDevice->down; + device->parent = parentDevice; parentDevice->down = device; device->node = node; device->down = NULL; device->name = strdup(val); - hal_probe_hal_services(device); + hal_probe_hal_services(device, true); if (0 != hal_invoke_service_op(device, "hal", HAL_LOAD_DTREE_CHILDREN, NULL, 0, NULL, NULL)) panic("hal_load_dtree_child_node: HAL_LOAD_DTREE_CHILDREN failed!"); @@ -58,50 +55,357 @@ static int hal_load_dtree_child_node(void* arg, dt_node_t* node) { return 0; } -static int hal_service_op(struct hal_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { +static int hal_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { if (method == HAL_LOAD_XNU_DTREE) { device->node = gDeviceTree; return 0; } else if (method == HAL_LOAD_DTREE_CHILDREN && device->node) { // int dt_parse(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, dt_node_t*), void* cbn_arg, int (*cb_prop)(void*, dt_node_t*, int, const char*, void*, uint32_t), void* cbp_arg) - return dt_parse(device->node, 0, NULL, hal_load_dtree_child_node, device, NULL, NULL); + return dt_parse_ex(device->node, 0, NULL, hal_load_dtree_child_node, device, NULL, NULL, 1); } else if (method == HAL_CREATE_CHILD_DEVICE && data_out_size && *data_out_size == 8) { struct hal_device* ndevice = malloc(sizeof(struct hal_device)); ndevice->next = device->down; + ndevice->parent = device; device->down = ndevice; ndevice->node = NULL; ndevice->down = NULL; ndevice->name = strdup(data_in); - hal_probe_hal_services(ndevice); + hal_probe_hal_services(ndevice, false); *(void**)data_out = ndevice; return 0; + } else if (method == HAL_GET_MAPPER && data_in && data_in_size == 4 && data_out && *data_out_size == 8) { + uint32_t index = *(uint32_t*)data_in; + *(void**)data_out = NULL; + + uint32_t len = 0; + dt_node_t* node = device->node; + if (!node) return -1; + + uint32_t* val = dt_prop(node, "iommu-parent", &len); + if (!val) { + return -1; + } + + if (index >= len / 4) { + return -1; + } + + uint32_t phandle = val[index]; + *(struct hal_device **)data_out = hal_get_phandle_device(phandle); + return 0; + } else if (method == HAL_MAP_REGISTERS && data_in && data_in_size == 4 && data_out && *data_out_size == 16) { + uint32_t index = *(uint32_t*)data_in; + ((void**)data_out)[0] = NULL; + ((void**)data_out)[1] = NULL; + + if (index < device->nr_device_maps && device->device_maps[index].size) { + struct device_regs * regs = &device->device_maps[index]; + ((void**)data_out)[0] = (void*)regs->base; + ((void**)data_out)[1] = (void*)regs->size; + return 0; + } + + uint32_t len = 0; + dt_node_t* node = device->node; + if (!node) return -1; + + void* val = dt_prop(node, "reg", &len); + if (!val) { + return -1; + } + + if (index * 0x10 >= len) { + return -1; + } + + struct device_regs * regs = val; + + uint64_t regbase = regs[index].base; + uint64_t size = regs[index].size; + + void* pmap = ((void**)data_out)[0] = (void*)hal_map_physical_mmio(regbase, size); + ((void**)data_out)[1] = (void*)regs[index].size; + + uint32_t old_maps_size = device->nr_device_maps; + uint32_t new_maps_size = old_maps_size; + if (!new_maps_size) new_maps_size = 8; + + while (index >= new_maps_size) { + new_maps_size *= 2; + } + + if (old_maps_size != new_maps_size) { + struct device_regs * new_map_regs = calloc(sizeof(struct device_regs), new_maps_size); + if (old_maps_size) { + memcpy(new_map_regs, device->device_maps, sizeof(struct device_regs) * old_maps_size); + } + device->device_maps = new_map_regs; + device->nr_device_maps = new_maps_size; + } + + device->device_maps[index].base = (uint64_t)pmap; + device->device_maps[index].size = size; + + + return 0; + } else if (method == HAL_DEVICE_CLOCK_GATE_ON || method == HAL_DEVICE_CLOCK_GATE_OFF) { + int32_t count = hal_get_clock_gate_size(device); + if (count > 0) { + for (int i=0; i < count; i++) { + int32_t clock_gate_id = hal_get_clock_gate_id(device, i); + if (clock_gate_id > 0) { + uint64_t clock = device_clock_by_id(clock_gate_id); + if (clock) { + clock_gate(clock, method == HAL_DEVICE_CLOCK_GATE_ON); + } + } + } + return 0; + } + return -1; } return -1; } +struct range_translation_entry { + uint64_t reg_base; + uint64_t phys_base; + uint64_t size; +} range_translation [64]; + +uint32_t range_translation_entries = 0; + +uint64_t translate_register_address(uint64_t address) { + for (int i=0; i < range_translation_entries; i++) { + if (address >= range_translation[i].reg_base && address < range_translation[i].reg_base + range_translation[i].size) { + return address - range_translation[i].reg_base + range_translation[i].phys_base; + } + } + panic("couldn't find address %llx in arm-io map", address); + return -1; +} + +int hal_apply_tunables(struct hal_device* device, const char* tunable_dt_entry_name) { + if (!tunable_dt_entry_name) { + tunable_dt_entry_name = "tunables"; + } + uint32_t len = 0; + dt_node_t* node = device->node; + if (!node) return -1; + + struct tunable_array { + uint32_t reg_index; + uint32_t reg_offset; + uint32_t reg_bits_to_clear; + uint32_t reg_bits_to_set; + }; + + struct tunable_array* val = dt_prop(node, tunable_dt_entry_name, &len); + if (!val) { + return -1; + } + if (len & 0xf) { + panic("hal_apply_tunables: my understanding of tunables is 4x uint32_t, but len is not a multiple of 0x10..."); + } + + uint32_t tunable_cnt = len / 0x10; + for (uint32_t i=0; i < tunable_cnt; i++) { + size_t sz = 0; + uint64_t regbase = (uint64_t) hal_map_registers(device, val[i].reg_index, &sz); + if (!regbase) { + panic("hal_apply_tunables: invalid reg_index (%d)", val[i].reg_index); + } + if ((val[i].reg_offset + 4) > sz) { + panic("hal_apply_tunables: OOB access (%d > %d)", val[i].reg_offset, sz); + } + uint32_t value = *(volatile uint32_t*)(regbase + val[i].reg_offset); + + value &= ~val[i].reg_bits_to_clear; + value |= val[i].reg_bits_to_set; + + *(volatile uint32_t*)(regbase + val[i].reg_offset) = value; + } + return 0; +} + +uint64_t hal_map_physical_mmio(uint64_t regbase, uint64_t size) { + regbase = translate_register_address(regbase); + uint64_t offset = regbase & 0x3fff; + regbase &= ~0x3fff; + + size += offset; + + size += 0x3FFF; + size &= ~0x3FFF; + uint64_t va = linear_kvm_alloc(size); + + map_range_map((uint64_t*)kernel_vm_space.ttbr0, va, regbase, size, 3, 0, 1, 0, PROT_READ|PROT_WRITE, !!(va & 0x7000000000000000)); + + for (uint32_t i=0; i < size; i+=0x1000) { + vm_flush_by_addr_all_asid(va + i); + } + + return va + offset; +} + +int32_t hal_get_clock_gate_id(struct hal_device* device, uint32_t index) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (!node) return -1; + + int32_t* val = dt_prop(node, "clock-gates", &len); + if (!val || index * 4 >= len) { + return -1; + } + return val[index]; +} +int32_t hal_get_clock_gate_size(struct hal_device* device) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (!node) return -1; + dt_prop(node, "clock-gates", &len); + return len / 4; +} +extern struct hal_device* gInterruptDevice; +int32_t hal_get_irqno(struct hal_device* device, uint32_t index) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (!node) return -1; + +#if 0 + int32_t* pval = dt_prop(node, "interrupt-parent", &len); + int32_t irqparent = *pval; + + if (hal_get_phandle_device(irqparent) != gInterruptDevice) { + panic("hal_get_irqno: only supported for AIC interrupt children"); + } +#endif + + int32_t* val = dt_prop(node, "interrupts", &len); + if (!val || index * 4 >= len) { + return -1; + } + return val[index]; +} +bool hal_unmask_interrupt(struct hal_device* device, uint32_t reg) { + if (hal_invoke_service_op(device, "irq", IRQ_UNMASK, ®, 4, NULL, 0)) { + return false; + } + return true; +} +bool hal_mask_interrupt(struct hal_device* device, uint32_t reg) { + if (hal_invoke_service_op(device, "irq", IRQ_MASK, ®, 4, NULL, 0)) { + return false; + } + return true; +} +bool hal_ack_interrupt(struct hal_device* device, uint32_t reg) { + if (hal_invoke_service_op(device, "irq", IRQ_ACK, ®, 4, NULL, 0)) { + return false; + } + return true; +} +bool hal_register_interrupt(struct hal_device* device, struct task* task, uint32_t irqno, void* context) { + struct irq_register_args args = { + .irq = irqno, + .task = task, + .context = context + }; + if (hal_invoke_service_op(device, "irq", IRQ_REGISTER, &args, sizeof(args), NULL, 0)) { + return false; + } + return true; +} +bool hal_controller_unmask_interrupt(struct hal_device* device, uint32_t reg) { + if (hal_invoke_service_op(device, "irqctrl", IRQ_UNMASK, ®, 4, NULL, 0)) { + return false; + } + return true; +} +bool hal_controller_mask_interrupt(struct hal_device* device, uint32_t reg) { + if (hal_invoke_service_op(device, "irqctrl", IRQ_MASK, ®, 4, NULL, 0)) { + return false; + } + return true; +} +bool hal_controller_ack_interrupt(struct hal_device* device, uint32_t reg) { + if (hal_invoke_service_op(device, "irqctrl", IRQ_ACK, ®, 4, NULL, 0)) { + return false; + } + return true; +} +bool hal_controller_register_interrupt(struct hal_device* device, struct task* task, uint32_t irqno, void* context) { + struct irq_register_args args = { + .irq = irqno, + .task = task, + .context = context + }; + if (hal_invoke_service_op(device, "irqctrl", IRQ_REGISTER, &args, sizeof(args), NULL, 0)) { + return false; + } + return true; +} + +void * hal_map_registers(struct hal_device* device, uint32_t index, size_t *size) { + struct { + void* base; + size_t size; + } rv; + size_t osz = 0x10; + if (size) *size = 0; + + if (hal_invoke_service_op(device, "hal", HAL_MAP_REGISTERS, &index, 4, &rv, &osz)) { + return NULL; + } + if (size) *size = rv.size; + return rv.base; +} +struct hal_device * hal_get_mapper(struct hal_device* device, uint32_t index) { + struct hal_device * rv = NULL; + size_t osz = 8; + + if (hal_invoke_service_op(device, "hal", HAL_GET_MAPPER, &index, 4, &rv, &osz)) { + return NULL; + } + return rv; +} + static bool hal_service_probe(struct hal_service* svc, struct hal_device* device, void** context) { return true; } struct hal_service hal_service = { .name = "hal", .probe = hal_service_probe, - .service_op = hal_service_op + .service_op = hal_service_op, + .flags = SERVICE_FLAGS_EARLY_PROBE }; int hal_invoke_service_op(struct hal_device* device, const char* svc_name, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { struct hal_device_service* svc = device->services; + + bool metaservice_lookup = false; + if (((method & METASERVICE_TAG_MASK) == HAL_METASERVICE_TAG)) { + metaservice_lookup = true; + } + while (svc) { - if (svc_name == svc->name || strcmp(svc_name, svc->name) == 0) { - return svc->service->service_op(svc->service, device, method, data_in, data_in_size, data_out, data_out_size); + if (strcmp(svc_name, svc->name) == 0 || metaservice_lookup) { + if (svc->service->service_op) { + int rv = svc->service->service_op(svc, device, method, data_in, data_in_size, data_out, data_out_size); + if (!metaservice_lookup) { + return rv; + } + } } svc = svc->next; } return -0xfe; } + struct hal_service* hal_service_head; lock hal_service_lock; @@ -111,23 +415,52 @@ void hal_register_hal_service(struct hal_service* svc) { hal_service_head = svc; lock_release(&hal_service_lock); } -void hal_probe_hal_services(struct hal_device* device) { +void hal_associate_service(struct hal_device* device, struct hal_service* svc, void* ctx) { + struct hal_device_service* hds = malloc(sizeof(struct hal_device_service)); + hds->name = svc->name; + hds->service = svc; + hds->context = ctx; + hds->next = device->services; + device->services = hds; +} +void hal_probe_hal_services(struct hal_device* device, bool isEarlyProbe) { lock_take(&hal_service_lock); + + if (device && device->node) { + uint32_t llen = 0; + uint32_t* phandle = dt_prop(device->node, "AAPL,phandle", &llen); + if (phandle && llen == 4) { + hal_register_phandle_device(*phandle, device); + device->phandle = *phandle; + } + } + struct hal_service* svc = hal_service_head; while (svc) { + if (!(svc->flags & SERVICE_FLAGS_EARLY_PROBE)) { + if (isEarlyProbe) { + svc = svc->next; + continue; + } + } else { + if (device->flags & DEVICE_HAS_BEEN_PROBED_EARLY) { + svc = svc->next; + continue; + } + } if (svc->probe) { void* ctx = NULL; if (svc->probe(svc, device, &ctx)) { - struct hal_device_service* hds = malloc(sizeof(struct hal_device_service)); - hds->name = svc->name; - hds->service = svc; - hds->context = ctx; - hds->next = device->services; - device->services = hds; + hal_associate_service(device, svc, ctx); } } svc = svc->next; } + + if (isEarlyProbe) { + device->flags |= DEVICE_HAS_BEEN_PROBED_EARLY; + } + lock_release(&hal_service_lock); } struct hal_platform _gPlatform; @@ -177,10 +510,153 @@ void lsdev_cmd(const char *cmd, char *args) recurse_device(gRootDevice, 0, lsdev_cb); } + +void hal_late_probe_hal_services_cb(struct hal_device* dev, int depth) { + if (gDeviceTreeDevice == dev) return; + + hal_probe_hal_services(dev, false); +} +void hal_late_probe_hal_services() { + recurse_device(gDeviceTreeDevice, 0, hal_late_probe_hal_services_cb); +} +void hal_issue_recursive_start_cb(struct hal_device* dev, int depth) { + hal_invoke_service_op(dev, "hal", HAL_METASERVICE_START, NULL, 0, NULL, NULL); +} +void hal_issue_recursive_start() { + recurse_device(gDeviceTreeDevice, 0, hal_issue_recursive_start_cb); +} + +static struct hal_device * hal_device_by_name_recursive(struct hal_device* dev, int depth, const char* name) { + struct hal_device* nxt = dev->down; + if (strcmp(dev->name, name) == 0) { + return dev; + } + + while (nxt) { + struct hal_device* rv = hal_device_by_name_recursive(nxt, depth+1, name); + if (rv) { + return rv; + } + nxt = nxt->next; + } + return NULL; +} +struct hal_device * hal_device_by_name(const char* name) { + return hal_device_by_name_recursive(gRootDevice, 0, name); +} + +struct hal_device** phandle_table; +uint32_t phandle_table_size; + +void hal_register_phandle_device(uint32_t phandle, struct hal_device* dev) { + if (!phandle_table_size) { + phandle_table_size = 0x100; + phandle_table = calloc(phandle_table_size, 8); + } + + uint32_t phandle_table_size_new = phandle_table_size; + while (phandle > phandle_table_size_new) { + phandle_table_size_new *= 2; + } + + if (phandle_table_size != phandle_table_size_new) { + struct hal_device** phandle_table_new = calloc(phandle_table_size_new, 8); + memcpy(phandle_table_new, phandle_table, phandle_table_size_new * 8); + phandle_table = phandle_table_new; + phandle_table_size = phandle_table_size_new; + } + + phandle_table[phandle] = dev; +} + + +struct hal_device* hal_get_phandle_device(uint32_t phandle) { + while (phandle < phandle_table_size) { + return phandle_table[phandle]; + } + return NULL; +} + +static bool irq_probe(struct hal_service* svc, struct hal_device* device, void** context) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (node) { + int32_t* pval = dt_prop(node, "interrupt-parent", &len); + if (pval) { + int32_t irqparent = *pval; + *context = (void*)(uintptr_t)irqparent; + return true; + } + } + return false; +} + +static int irq_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + struct hal_device* irq_ctrl_parent = hal_get_phandle_device((uint32_t) svc->context); + if (irq_ctrl_parent) { + if (method == IRQ_MASK && data_in_size == 4) { + uint32_t irq_index = *(uint32_t*)data_in; + int32_t irq_number = hal_get_irqno(device, irq_index); + if (irq_number < 0) { + return -1; + } + + return hal_controller_mask_interrupt(irq_ctrl_parent, irq_number) == 0 ? true : false; // redirect request to interrupt controller + } else if (method == IRQ_UNMASK && data_in_size == 4) { + uint32_t irq_index = *(uint32_t*)data_in; + int32_t irq_number = hal_get_irqno(device, irq_index); + if (irq_number < 0) { + return -1; + } + return hal_controller_unmask_interrupt(irq_ctrl_parent, irq_number) == 0 ? true : false; // redirect request to interrupt controller + } else if (method == IRQ_ACK) { + uint32_t irq_index = *(uint32_t*)data_in; + int32_t irq_number = hal_get_irqno(device, irq_index); + if (irq_number < 0) { + return -1; + } + return hal_controller_ack_interrupt(irq_ctrl_parent, irq_number) == 0 ? true : false; // redirect request to interrupt controller + } else if (method == IRQ_REGISTER && data_in_size == sizeof(struct irq_register_args)) { + struct irq_register_args* args = data_in; + + int32_t irq_number = hal_get_irqno(device, args->irq); + if (irq_number < 0) { + return -1; + } + return hal_controller_register_interrupt(irq_ctrl_parent, args->task, irq_number, args->context) == 0 ? true : false; // redirect request to interrupt controller + } + } + return -1; +} + +static struct hal_service irq_svc = { + .name = "irq", + .probe = irq_probe, + .service_op = irq_service_op +}; + + void hal_init() { gPlatform = NULL; gRootDevice = &_gRootDevice; + dt_node_t* dev = dt_find(gDeviceTree, "arm-io"); + if (!dev) panic("invalid devicetree: no arm-io!"); + uint32_t len = 0; + + uint64_t* val = dt_prop(dev, "ranges", &len); + if (!val) panic("invalid devicetree: no prop!"); + + len /= 0x18; + + for (int i=0; i < len; i++) { // basically a memcpy but for clarity... + range_translation[i].reg_base = val[i*3]; + range_translation[i].phys_base = val[i*3 + 1]; + range_translation[i].size = val[i*3 + 2]; + range_translation_entries++; + if (range_translation_entries > 64) panic("too many entries in arm-io"); + } + extern struct driver drivers[] __asm("section$start$__DATA$__drivers"); extern struct driver drivers_end[] __asm("section$end$__DATA$__drivers"); struct driver* driver = &drivers[0]; @@ -210,19 +686,43 @@ void hal_init() { } hal_init_late(); hal_register_hal_service(&hal_service); + hal_register_hal_service(&irq_svc); - hal_probe_hal_services(gRootDevice); + hal_probe_hal_services(gRootDevice, true); size_t ssz = 8; if (0 != hal_invoke_service_op(gRootDevice, "hal", HAL_CREATE_CHILD_DEVICE, "dtree", 6, &gDeviceTreeDevice, &ssz)) panic("hal_init: HAL_CREATE_CHILD_DEVICE failed!"); - if (0 != hal_invoke_service_op(gDeviceTreeDevice, "hal", HAL_LOAD_XNU_DTREE, NULL, 0, NULL, NULL)) panic("hal_init: HAL_LOAD_XNU_DTREE failed!"); if (0 != hal_invoke_service_op(gDeviceTreeDevice, "hal", HAL_LOAD_DTREE_CHILDREN, NULL, 0, NULL, NULL)) panic("hal_init: HAL_LOAD_DTREE_CHILDREN failed!"); + if (gPlatform->bound_platform_driver->late_init) { + gPlatform->bound_platform_driver->late_init(); + } + + hal_late_probe_hal_services(); + command_register("lsdev", "prints hal devices tree", lsdev_cmd); - } +bool hal_device_is_compatible(struct hal_device* device, const char* name) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (!node) return false; + + char* compat = dt_prop(node, "compatible", &len); + if (!compat) { + return false; + } + + char* compatend = compat + len; + while (compat < compatend) { + if (strcmp(compat, name) == 0) { + return true; + } + compat += strlen(compat) + 1; + } + return false; +} diff --git a/src/drivers/hal/hal.h b/src/drivers/hal/hal.h index 2b6a0949..63849b48 100644 --- a/src/drivers/hal/hal.h +++ b/src/drivers/hal/hal.h @@ -47,15 +47,29 @@ struct hal_platform_driver { void* context; bool (*probe)(struct hal_platform_driver* device_driver, struct hal_platform* device); bool (*get_platform_value)(const char* name, void* value, size_t* size); + void (*late_init)(); +}; +struct device_regs { + uint64_t base; + uint64_t size; }; - struct hal_device { struct hal_device* next; struct hal_device* down; + struct hal_device* parent; const char* name; dt_node_t* node; struct hal_device_service* services; + + struct device_regs * device_maps; + uint32_t nr_device_maps; + + uint32_t phandle; + uint32_t flags; + + struct hal_service* ioprovider; }; +#define DEVICE_HAS_BEEN_PROBED_EARLY 1 struct hal_device_service { struct hal_device_service* next; @@ -68,10 +82,13 @@ struct hal_service { struct hal_service* next; const char* name; bool (*probe)(struct hal_service* svc, struct hal_device* device, void** context); - int (*service_op)(struct hal_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size); + int (*service_op)(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size); + int flags; }; - +#define SERVICE_FLAGS_EARLY_PROBE 1 extern void hal_register_hal_service(struct hal_service* svc); +extern void hal_register_phandle_device(uint32_t phandle, struct hal_device* dev); +extern struct hal_device* hal_get_phandle_device(uint32_t phandle); struct hal_platform { uint32_t cpid; @@ -87,3 +104,54 @@ extern void hal_register_platform_driver(struct hal_platform_driver* driver); extern const char* hal_platform_name(); extern bool hal_get_platform_value(const char* name, void* value, size_t* size); extern int hal_invoke_service_op(struct hal_device* device, const char* svc_name, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size); +extern struct hal_device * hal_get_mapper(struct hal_device* device, uint32_t index); +extern struct hal_device * hal_device_by_name(const char* name); +extern void * hal_map_registers(struct hal_device* device, uint32_t index, size_t *size); +extern int32_t hal_get_irqno(struct hal_device* device, uint32_t index); +extern uint64_t hal_map_physical_mmio(uint64_t regbase, uint64_t size); +extern int32_t hal_get_clock_gate_id(struct hal_device* device, uint32_t index); +extern int32_t hal_get_clock_gate_size(struct hal_device* device); +extern int hal_apply_tunables(struct hal_device* device, const char* tunable_dt_entry_name); +extern void hal_associate_service(struct hal_device* device, struct hal_service* svc, void* ctx); +extern void hal_device_set_io_provider(struct hal_device* device, struct hal_service* svc); +extern bool hal_device_is_compatible(struct hal_device* device, const char* name); + +extern bool hal_unmask_interrupt(struct hal_device* device, uint32_t reg); +extern bool hal_mask_interrupt(struct hal_device* device, uint32_t reg); +extern bool hal_ack_interrupt(struct hal_device* device, uint32_t reg); +extern bool hal_register_interrupt(struct hal_device* device, struct task* task, uint32_t irqno, void* context); + +extern bool hal_controller_unmask_interrupt(struct hal_device* device, uint32_t reg); +extern bool hal_controller_mask_interrupt(struct hal_device* device, uint32_t reg); +extern bool hal_controller_ack_interrupt(struct hal_device* device, uint32_t reg); +extern bool hal_controller_register_interrupt(struct hal_device* device, struct task* task, uint32_t irqno, void* context); + +#define HAL_LOAD_XNU_DTREE 0 +#define HAL_LOAD_DTREE_CHILDREN 1 +#define HAL_CREATE_CHILD_DEVICE 2 +#define HAL_GET_MAPPER 3 +#define HAL_MAP_REGISTERS 4 +#define HAL_GET_IRQNR 5 +#define HAL_DEVICE_CLOCK_GATE_ON 6 +#define HAL_DEVICE_CLOCK_GATE_OFF 7 + + +// Abstract IRQ interface + +#define IRQ_REGISTER 0 +#define IRQ_MASK 1 +#define IRQ_UNMASK 2 +#define IRQ_ACK 3 + +struct irq_register_args { + struct task* task; + uint32_t irq; + void* context; +}; + + +#define METASERVICE_TAG_MASK 0xf0000000 + +#define HAL_METASERVICE_TAG 0x80000000 +#define HAL_METASERVICE_START (HAL_METASERVICE_TAG | 0x1) + diff --git a/src/drivers/i2c/i2c.c b/src/drivers/i2c/i2c.c new file mode 100644 index 00000000..4b0649de --- /dev/null +++ b/src/drivers/i2c/i2c.c @@ -0,0 +1,32 @@ + +#import +#import "i2c.h" + +struct i2c_cmd* i2c_cmd_create(uint16_t txno) { + struct i2c_cmd* cmd = calloc(sizeof(struct i2c_cmd) + sizeof(struct i2c_tx) * txno, 1); + cmd->txno = txno; + return cmd; +} +void i2c_cmd_destroy(struct i2c_cmd* cmd) { + free(cmd); +} + +void i2c_cmd_set_write_tx(struct i2c_cmd* cmd, uint16_t index, uint16_t address, const void* base, uint16_t size) { + cmd->txes[index].buf = (void*)base; + cmd->txes[index].size = size; + cmd->txes[index].addr = address; + cmd->txes[index].readwrite = true; +} + +void i2c_cmd_set_read_tx(struct i2c_cmd* cmd, uint16_t index, uint16_t address, void* base, uint16_t size) { + cmd->txes[index].buf = base; + cmd->txes[index].size = size; + cmd->txes[index].addr = address; + cmd->txes[index].readwrite = false; +} +bool i2c_cmd_perform(struct hal_device* i2c_dev, struct i2c_cmd* cmd){ + if ( ! hal_invoke_service_op(i2c_dev, "i2c", I2C_CMD_PERFORM, (void*)cmd, I2C_CMD_PERFORM_SIZE, NULL, NULL) ) { + return true; + } + return false; +} diff --git a/src/drivers/i2c/i2c.h b/src/drivers/i2c/i2c.h new file mode 100644 index 00000000..f44011b3 --- /dev/null +++ b/src/drivers/i2c/i2c.h @@ -0,0 +1,22 @@ +struct i2c_ctx; + +struct i2c_tx { + void* buf; + uint16_t size; + uint16_t addr; + bool readwrite; // true = write, false = read +}; + +struct i2c_cmd { + uint16_t txno; + struct i2c_tx txes[]; +}; + +#define I2C_CMD_PERFORM 1 +#define I2C_CMD_PERFORM_SIZE 0xFFFFFFFF + +extern struct i2c_cmd* i2c_cmd_create(uint16_t txno); +extern void i2c_cmd_destroy(struct i2c_cmd* cmd); +extern void i2c_cmd_set_write_tx(struct i2c_cmd* cmd, uint16_t index, uint16_t address, const void* base, uint16_t size); +extern void i2c_cmd_set_read_tx(struct i2c_cmd* cmd, uint16_t index, uint16_t address, void* base, uint16_t size); +extern bool i2c_cmd_perform(struct hal_device* i2c_dev, struct i2c_cmd* cmd); diff --git a/src/drivers/i2c/i2c_8940x.c b/src/drivers/i2c/i2c_8940x.c new file mode 100644 index 00000000..64a8e876 --- /dev/null +++ b/src/drivers/i2c/i2c_8940x.c @@ -0,0 +1,238 @@ +#import + +struct i2c_8940x_ctx { + uint64_t i2c_regbase; + struct hal_device* device; + lock i2c_lock; + + uint8_t* data_in; + uint16_t data_in_size; + + struct event irq_event; + struct task* irq_task; + + uint32_t rdreg; +}; + +__unused static uint32_t i2c_8940x_reg_read(struct i2c_8940x_ctx* i2c, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(i2c->i2c_regbase + offset); +#ifdef REG_LOG + fiprintf(stderr, "i2c_8940x_8940x_reg_read(%x) == %x\n", offset, rv); +#endif + return rv; +} + +__unused static void i2c_8940x_reg_write(struct i2c_8940x_ctx* i2c, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "i2c_8940x_8940x_reg_write(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(i2c->i2c_regbase + offset) = value; +} + +__unused static void i2c_8940x_reg_or(struct i2c_8940x_ctx* i2c, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "i2c_8940x_reg_or(%x) |= %x\n", offset, value); +#endif + *(volatile uint32_t *)(i2c->i2c_regbase + offset) |= value; +} + +static void i2c_8940x_init_regs(struct i2c_8940x_ctx* i2c) { + i2c_8940x_reg_write(i2c, 0x1c, 4); + i2c_8940x_reg_write(i2c, 0x14, 0xAA00040); + i2c_8940x_reg_write(i2c, 0x18, 0); + i2c_8940x_reg_write(i2c, 0x10, 0x80000000); + + uint32_t hwrev = i2c_8940x_reg_read(i2c, 0x28); + if (hwrev >= 6) { + i2c_8940x_reg_or(i2c, 0x1c, 0x800); + } + + /* + TBD: filter-tunable and tbuf-tunable impl + */ +} + +__unused static void i2c_8940x_lockunlock(struct i2c_8940x_ctx* i2c, bool lockunlock) { + if (lockunlock) { + i2c_8940x_reg_write(i2c, 0x44, 1); + } else { + i2c_8940x_reg_write(i2c, 0x44, 0); + } +} + +static void i2c_8940x_irq_task() { + while (1) { + // fiprintf(stderr, "i2c irq\n"); + struct i2c_8940x_ctx* i2c = task_current_interrupt_context(); + i2c_8940x_reg_write(i2c, 0x18, 0); + i2c->rdreg = i2c_8940x_reg_read(i2c, 0x14); + i2c_8940x_reg_write(i2c, 0x14, i2c->rdreg); + event_fire(&i2c->irq_event); + task_exit_irq(); + } + +} + +static bool i2c_8940x_read(struct i2c_8940x_ctx* i2c, uint16_t addr, void* data_in, uint16_t size) { + uint32_t rg = i2c_8940x_reg_read(i2c, 0x2C); + rg &= 0xFFFF00FF; + rg |= ((size << 8) & 0xFF00); + i2c_8940x_reg_write(i2c, 0x2C, rg); + + i2c_8940x_reg_write(i2c, 0, ((addr << 1) & 0xFE) | 0x101); + i2c_8940x_reg_write(i2c, 0, size | 0x600); + + uint8_t* data_in_c = data_in; + uint16_t data_cursor = 0; + + while (1) { + disable_interrupts(); + i2c_8940x_reg_write(i2c, 0x18, 0xB00040); + i2c->rdreg = 0; + event_wait_asserted(&i2c->irq_event); + + if (i2c->rdreg & 0xA00040) { + uint32_t rdreg = i2c_8940x_reg_read(i2c, 0x14); + i2c_8940x_reg_write(i2c, 0x14, rdreg); + i2c_8940x_reg_write(i2c, 0x10, 0x80000000); + return false; + } + + uint8_t readb = i2c_8940x_reg_read(i2c, 0x8) >> 8; + for (uint8_t i = 0; i < readb; i++) { + if (data_cursor >= size) { + panic("i2c_8940x_read: ???"); + } + data_in_c[data_cursor++] = i2c_8940x_reg_read(i2c, 4); + } + + if (data_cursor >= size) { + break; + } + } + + return true; +} + +static bool i2c_8940x_write(struct i2c_8940x_ctx* i2c, uint16_t addr, void* data_out, uint16_t size) { + uint8_t* data_out_c = data_out; + + i2c_8940x_reg_write(i2c, 0, ((addr << 1) & 0xFE) | 0x100); + + i2c_8940x_lockunlock(i2c, true); + for (uint32_t i=0; i < size; i++) { + if (i && !(i & 0xF)) { + // fifo depth is not too great + i2c_8940x_lockunlock(i2c, false); + + while (1) { + disable_interrupts(); + i2c_8940x_reg_write(i2c, 0x18, 0x8800040); + i2c->rdreg = 0; + event_wait_asserted(&i2c->irq_event); + if (i2c->rdreg & 0x800040) { + uint32_t rdreg = i2c_8940x_reg_read(i2c, 0x14); + i2c_8940x_reg_write(i2c, 0x14, rdreg); + i2c_8940x_reg_write(i2c, 0x10, 0x80000000); + return false; + } + if (i2c->rdreg & 0x08000000) { + // tx queue space available + break; + } + } + + i2c_8940x_lockunlock(i2c, true); + } + i2c_8940x_reg_write(i2c, 0, data_out_c[i] | (i+1 == size ? 0x200 : 0)); + } + i2c_8940x_lockunlock(i2c, false); + return true; +} + +static bool i2c_8960x_command_perform(struct i2c_8940x_ctx* i2c, struct i2c_cmd* cmd) { + lock_take(&i2c->i2c_lock); + + i2c_8940x_init_regs(i2c); + + for (uint16_t i = 0; i < cmd->txno; i++) { + if (cmd->txes[i].readwrite) { + // write + if (!i2c_8940x_write(i2c, cmd->txes[i].addr, cmd->txes[i].buf, cmd->txes[i].size)) { + lock_release(&i2c->i2c_lock); + return false; + } + } else { + // read + if (!i2c_8940x_read(i2c, cmd->txes[i].addr, cmd->txes[i].buf, cmd->txes[i].size)) { + lock_release(&i2c->i2c_lock); + return false; + } + } + } + + lock_release(&i2c->i2c_lock); + return true; +} + + +static bool register_8940x_i2c(struct hal_device* device, void** context) { + // S5L8940x I2C controller + + struct i2c_8940x_ctx* i2c = calloc(sizeof(struct i2c_8940x_ctx), 1); + + dt_node_t* node = device->node; + + uint32_t len = 0; + void* name = dt_prop(node, "name", &len); + + i2c->device = device; + i2c->i2c_regbase = (uint64_t)hal_map_registers(i2c->device, 0, NULL); + + hal_invoke_service_op(i2c->device, "hal", HAL_DEVICE_CLOCK_GATE_ON, NULL, 0, NULL, NULL); // turn on I2C controller + + i2c->irq_task = task_create_extended(name, i2c_8940x_irq_task, TASK_IRQ_HANDLER|TASK_PREEMPT, 0); + + *context = i2c; + + return true; +} + +static int i2c_8940x_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + if (method == HAL_METASERVICE_START) { + struct i2c_8940x_ctx* i2c = svc->context; + + if (!hal_register_interrupt(device, i2c->irq_task, 0, i2c)) + panic("i2c_8940x_start: hal_register_interrupt failed!"); + + return 0; + } else if (method == I2C_CMD_PERFORM && data_in && data_in_size == I2C_CMD_PERFORM_SIZE){ + if (i2c_8960x_command_perform(svc->context, data_in)) { + return 0; + } + return -1; + } + return -1; +} + +static bool i2c_8940x_probe(struct hal_service* svc, struct hal_device* device, void** context) { + if (hal_device_is_compatible(device, "i2c,s5l8940x")) { + if (register_8940x_i2c(device, context)) { + return true; + } + } + return false; + +} + +static struct hal_service i2c_8940x_svc = { + .name = "i2c", + .probe = i2c_8940x_probe, + .service_op = i2c_8940x_service_op +}; + +static void i2c_8940x_init(struct driver* driver) { + hal_register_hal_service(&i2c_8940x_svc); +} + +REGISTER_DRIVER(i2c_8940x, i2c_8940x_init, NULL, 0); diff --git a/src/drivers/i2c/i2c_8940x.h b/src/drivers/i2c/i2c_8940x.h new file mode 100644 index 00000000..e69de29b diff --git a/src/drivers/mipi/mipi.c b/src/drivers/mipi/mipi.c index 8444ce09..e647bf09 100644 --- a/src/drivers/mipi/mipi.c +++ b/src/drivers/mipi/mipi.c @@ -95,11 +95,10 @@ void mipi_cmd(const char* cmd, char* args) { } } -void mipi_init() { - if(dt_find(gDeviceTree, "mipi-dsim")) { - uint64_t mipi_reg = dt_get_u32_prop("mipi-dsim", "reg"); - mipi_reg += gIOBase; - gmipi_reg = mipi_reg; +void mipi_init() { + struct hal_device* mipi_dev = hal_device_by_name("mipi-dsim"); + if (mipi_dev) { + gmipi_reg = (uint64_t) hal_map_registers(mipi_dev, 0, NULL); command_register("mipi", "mipi tools", mipi_cmd); } } diff --git a/src/drivers/plat/t8103.c b/src/drivers/plat/t8103.c new file mode 100644 index 00000000..842aff51 --- /dev/null +++ b/src/drivers/plat/t8103.c @@ -0,0 +1,74 @@ +/* + * pongoOS - https://checkra.in + * + * Copyright (C) 2019-2020 checkra1n team + * + * This file is part of pongoOS. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#import + +static bool t8103_probe(struct hal_platform_driver* device_driver, struct hal_platform* device) { + if (device->cpid == 0x8103) { + return true; + } + return false; +} + +static struct usb_regs t8103_usb_regs = { + .reg1 = 0x32080270, + .reg2 = 0x32080278, + .reg3 = 0x32080270, + .otg_irq = 0x307 +}; + +static struct usb_dart_regs t8103_usb_dart_regs = { + //.synopsysOTGBase = 0x239000000, +}; + +static bool t8103_get_platform_value(const char* name, void* value, size_t* size) { + if (strcmp(name, "usb_regs") == 0 && *size == sizeof(struct usb_regs)) { + return false; // not yet + + memcpy(value, &t8103_usb_regs, sizeof(struct usb_regs)); + return true; + } else + if (strcmp(name, "usb_dart") == 0 && *size == sizeof(struct usb_dart_regs)) { + return false; // not yet + + memcpy(value, &t8103_usb_dart_regs, sizeof(struct usb_dart_regs)); + return true; + } + return false; +} + +static struct hal_platform_driver t8103_plat = { + .name = "Apple M1 (T8103)", + .context = NULL, + .probe = t8103_probe, + .get_platform_value = t8103_get_platform_value +}; + +static void t8103_init(struct driver* driver) { + hal_register_platform_driver(&t8103_plat); +} + +REGISTER_DRIVER(t8103, t8103_init, NULL, DRIVER_FLAGS_PLATFORM); diff --git a/src/drivers/uart/uart.c b/src/drivers/uart/uart.c index 7bd4efc1..709f385b 100644 --- a/src/drivers/uart/uart.c +++ b/src/drivers/uart/uart.c @@ -90,11 +90,13 @@ static inline void put_serial_modifier(const char* str) { } uint64_t gUartBase; +struct hal_device* gUartDevice; extern uint32_t gLogoBitmap[32]; void serial_early_init() { + gUartDevice = hal_device_by_name("uart0"); + gUartBase = (uint64_t) hal_map_registers(gUartDevice, 0, NULL); + disable_interrupts(); - gUartBase = dt_get_u32_prop("uart0", "reg"); - gUartBase += gIOBase; rULCON0 = 3; rUCON0 = 0x405; rUFCON0 = 0; @@ -125,6 +127,9 @@ void serial_pinmux_init() { // Pinmux debug UART on ATV4K // This will also pinmux uart0 on iPad Pro 2G if((strcmp(soc_name, "t8011") == 0)) { + struct hal_device* gpio_dev = hal_device_by_name("gpio"); + uint64_t gGpioBase = (uint64_t) hal_map_registers(gpio_dev, 0, NULL); + rT8011TX = UART_TX_MUX; rT8011RX = UART_RX_MUX; } @@ -140,9 +145,9 @@ void serial_enable_rx() { char uart_irq_driven = 0; void serial_init() { struct task* irq_task = task_create_extended("uart", uart_main, TASK_IRQ_HANDLER|TASK_PREEMPT, 0); - + disable_interrupts(); - uart_irq = dt_get_u32_prop("uart0", "interrupts"); + uart_irq = hal_get_irqno(gUartDevice, 0); serial_disable_rx(); task_bind_to_irq(irq_task, uart_irq); uart_irq_driven = 0; diff --git a/src/drivers/usb/synopsys_drd.c b/src/drivers/usb/synopsys_drd.c new file mode 100644 index 00000000..1c56cee0 --- /dev/null +++ b/src/drivers/usb/synopsys_drd.c @@ -0,0 +1,541 @@ +/* + * pongoOS - https://checkra.in + * + * Copyright (C) 2019-2020 checkra1n team + * + * This file is part of pongoOS. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +struct drd { + uint64_t regBase; + uint64_t configRegBase; + uint64_t pipeHandlerRegBase; + uint64_t coreEvtRegBase; + uint64_t ausbCtlRegBase; + uint64_t ausbBulkFabricRegBase; + uint64_t atcLinkRegBase; + uint64_t atcRegBase; + uint64_t atcRegBasePipe; + + uint64_t physBaseDMA; + void* virtBaseDMA; + + struct task* irq_task; + struct hal_device* atc_device; + struct hal_device* mapper; + struct hal_device* device; +}; + +__unused static uint32_t drd_reg_read(struct drd* drd, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(drd->regBase + offset); +#ifdef REG_LOG + fiprintf(stderr, "drd_reg_read(%x) = %x\n", offset, rv); +#endif + return rv; +} +__unused static uint32_t atc_reg_read(struct drd* drd, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(drd->atcRegBase + offset); +#ifdef REG_LOG + fiprintf(stderr, "atc_reg_read(%x) = %x\n", offset, rv); +#endif + return rv; +} +__unused static uint32_t atc3_reg_read(struct drd* drd, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(drd->atcRegBasePipe + offset); +#ifdef REG_LOG + fiprintf(stderr, "atc3_reg_read(%x) = %x\n", offset, rv); +#endif + return rv; +} +__unused static uint32_t pipehandler_reg_read(struct drd* drd, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(drd->pipeHandlerRegBase + offset); +#ifdef REG_LOG + fiprintf(stderr, "pipehandler_reg_read(%x) = %x\n", offset, rv); +#endif + return rv; +} +__unused static uint32_t ausb_reg_read(struct drd* drd, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(drd->ausbCtlRegBase + offset); +#ifdef REG_LOG + fiprintf(stderr, "ausb_reg_read(%x) = %x\n", offset, rv); +#endif + return rv; +} +__unused static uint32_t configreg_reg_read(struct drd* drd, uint32_t offset) { + uint32_t rv = *(volatile uint32_t *)(drd->configRegBase + offset); +#ifdef REG_LOG + fiprintf(stderr, "configreg_reg_read(%x) = %x\n", offset, rv); +#endif + return rv; +} + +__unused static void drd_reg_write(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "drd_reg_write(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->regBase + offset) = value; +} +__unused static void drd_reg_write64(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "drd_reg_write64(%x) = %x\n", offset, value); +#endif + *(volatile uint64_t *)(drd->regBase + offset) = value; +} +__unused static void atc_reg_write(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "atc_reg_write(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->atcRegBase + offset) = value; +} +__unused static void atc3_reg_write(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "atc3_reg_write(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->atcRegBasePipe + offset) = value; +} +__unused static void pipehandler_reg_write(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "pipehandler_reg_write(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->pipeHandlerRegBase + offset) = value; +} +__unused static void ausb_reg_write(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "ausb_reg_write(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->ausbCtlRegBase + offset) = value; +} + +__unused static void drd_reg_and(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "drd_reg_and(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->regBase + offset) &= value; +} +__unused static void atc_reg_and(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "atc_reg_and(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->atcRegBase + offset) &= value; +} +__unused static void pipehandler_reg_and(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "pipehandler_reg_and(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->pipeHandlerRegBase + offset) &= value; +} +__unused static void ausb_reg_and(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "ausb_reg_and(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->ausbCtlRegBase + offset) &= value; +} + +__unused static void drd_reg_or(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "drd_reg_or(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->regBase + offset) |= value; +} +__unused static void atc_reg_or(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "atc_reg_or(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->atcRegBase + offset) |= value; +} +__unused static void pipehandler_reg_or(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "pipehandler_reg_or(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->pipeHandlerRegBase + offset) |= value; +} +__unused static void ausb_reg_or(struct drd* drd, uint32_t offset, uint32_t value) { +#ifdef REG_LOG + fiprintf(stderr, "ausb_reg_or(%x) = %x\n", offset, value); +#endif + *(volatile uint32_t *)(drd->ausbCtlRegBase + offset) |= value; +} + +#include "synopsys_drd_regs.h" + +static void USB_DEBUG_PRINT_REGISTERS(struct drd* drd) { + disable_interrupts(); +#define USB_DEBUG_REG_VALUE(reg) fiprintf(stderr, ""#reg " = 0x%x\n", drd_reg_read(drd, reg)); +#define PHY_DEBUG_REG_VALUE(reg) fiprintf(stderr, ""#reg " = 0x%x\n", drd_reg_read(drd, reg)); + + PHY_DEBUG_REG_VALUE(AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL); + PHY_DEBUG_REG_VALUE(AUSBC_CFG_USB2PHY_BLK_USB2PHY_MISC_TUNE); + + USB_DEBUG_REG_VALUE(G_DCFG); + USB_DEBUG_REG_VALUE(G_DCTL); + USB_DEBUG_REG_VALUE(G_DSTS); + USB_DEBUG_REG_VALUE(G_GSNPSID); + USB_DEBUG_REG_VALUE(G_GCTL); + USB_DEBUG_REG_VALUE(G_GSTS); + USB_DEBUG_REG_VALUE(G_GPMSTS); + USB_DEBUG_REG_VALUE(G_GUSB2PHYCFG); + USB_DEBUG_REG_VALUE(G_GDBGFIFOSPACE); + USB_DEBUG_REG_VALUE(G_GEVNTADRLO(0)); + USB_DEBUG_REG_VALUE(G_GEVNTADRHI(0)); + USB_DEBUG_REG_VALUE(G_GEVNTCOUNT(0)); + USB_DEBUG_REG_VALUE(G_GEVNTSIZ(0)); + USB_DEBUG_REG_VALUE(G_GPMSTS); + USB_DEBUG_REG_VALUE(G_BUSERRADDR_LO); + USB_DEBUG_REG_VALUE(G_BUSERRADDR_HI); + USB_DEBUG_REG_VALUE(G_GSBUSCFG0); + + enable_interrupts(); +} + + +__unused static int8_t drd_device_generic_command(struct drd* drd, uint32_t cmd, uint32_t arg) { + drd_reg_write(drd, G_DGCMDPAR, arg); + drd_reg_write(drd, G_DGCMD, (cmd & DGCMD_CMDMASK) | DGCMD_CMDACT); + while (1) { + uint32_t rd = drd_reg_read(drd, G_DGCMD); + if (! (rd & DGCMD_CMDACT)) { + return (rd & DGCMD_CMDSTSMASK) >> DGCMD_CMDSTSSHIFT; + } + } +} +__unused static int8_t drd_endpoint_command(struct drd* drd, uint32_t ep, uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + drd_reg_write(drd, G_DEPCMDPAR0(ep), arg0); + drd_reg_write(drd, G_DEPCMDPAR1(ep), arg1); + drd_reg_write(drd, G_DEPCMDPAR2(ep), arg2); + drd_reg_write(drd, G_DEPCMD(ep), (cmd & DGCMD_CMDMASK)); + + drd_reg_or(drd, G_DEPCMD(ep), DEPCMD_CMDACT); + + while (!(cmd & DEPCMD_CMDIOC)) { + uint32_t rd = drd_reg_read(drd, G_DEPCMD(ep)); + if (! (rd & DEPCMD_CMDACT)) { + return (rd & DGCMD_CMDSTSMASK) >> DGCMD_CMDSTSSHIFT; + } + } + + return 0; +} + + +#define ENDPOINT_EP0_OUT 0 +#define ENDPOINT_EP0_IN 1 +#define ENDPOINT_EP1_OUT 2 +#define ENDPOINT_EP1_IN 3 + +void drd_endpoint_start_configuration(struct drd* drd, uint32_t ep, uint32_t rsrc) { + if (drd_endpoint_command(drd, ep, DEPSTARTCFG | DEPCMD_RESOURCE_INDEX(rsrc) , 0, 0, 0)) { + panic("drd_endpoint_set_configuration: drd_endpoint_command failed!"); + } +} +void drd_endpoint_set_configuration(struct drd* drd, uint32_t ep, uint32_t ep_type, uint32_t packetsz) { + if (drd_endpoint_command(drd, ep, DEPCFG, DEPCFG_ACTION_INITIALIZE | DEPCFG_MAX_PACKET_SIZE(packetsz) | DEPCFG_EP_TYPE(ep_type), DEPCFG_FIFO_BASED | DEPCFG_EP_NUMBER(ep) | DEPCFG_XFER_NOT_READY_EN | DEPCFG_XFER_COMPLETE_EN | DEPCFG_INTR_NUM(0), 0)) { + panic("drd_endpoint_set_configuration: drd_endpoint_command failed!"); + } +} + +static void drd_irq_handle() { + fiprintf(stderr, "drd irq\n"); + struct drd* drd = task_current_interrupt_context(); + + while (1) { + uint32_t elements = drd_reg_read(drd, G_GEVNTCOUNT(0)); + if (!elements) { + break; + } + + cache_invalidate(drd->virtBaseDMA, ((elements * 4) + 0x3f) & ~0x3f); + + volatile uint32_t* eventbuffer = drd->virtBaseDMA; + + for (uint32_t i = 0; i < elements; i++) { + uint32_t event = eventbuffer[0xfff - i]; + fiprintf(stderr, "drd event: %x\n", event); + } + + drd_reg_write(drd, G_GEVNTCOUNT(0), elements); + } +} + +static void drd_irq_task() { + while (1) { + disable_interrupts(); + drd_irq_handle(); + enable_interrupts(); + task_exit_irq(); + } + +} +bool is_in_host_mode = true; // default value + +__unused static void atc_enable_device(struct drd* drd, bool enable) { + uint32_t reg = 0; + if (enable) { + reg = (atc_reg_read(drd, AUSBC_CFG_USB2PHY_BLK_USB_CTL) & ~USB_MODE_MASK) | 2; + } else { + spin(5 * 1000); + reg = (atc_reg_read(drd, AUSBC_CFG_USB2PHY_BLK_USB_CTL) & ~USB_MODE_MASK) | 0; + } + atc_reg_write(drd, AUSBC_CFG_USB2PHY_BLK_USB_CTL, reg); + + hal_apply_tunables(drd->atc_device, "tunable-device"); + is_in_host_mode = false; +} + +__unused static void atc_enable_host(struct drd* drd, bool enable) { + uint32_t reg = 0; + if (enable) { + atc_reg_or(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_SIG, VBUS_DETECT_FORCE_VAL | VBUS_DETECT_FORCE_EN | VBUS_VALID_EXT_FORCE_VAL | VBUS_VALID_EXT_FORCE_EN); + spin(10 * 1000); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL, ~USB2PHY_SIDDQ); + spin(10 * 1000); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL, 0xFFFFFFFC); + atc_reg_or(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_SIG, USB2PHY_APB_RESETN); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_MISC_TUNE, 0x9FFFFFFFLL); + + reg = (atc_reg_read(drd, AUSBC_CFG_USB2PHY_BLK_USB_CTL) & ~USB_MODE_MASK) | 2; + atc_reg_write(drd, AUSBC_CFG_USB2PHY_BLK_USB_CTL, reg); + + } else { + spin(5 * 1000); + reg = (atc_reg_read(drd, AUSBC_CFG_USB2PHY_BLK_USB_CTL) & ~USB_MODE_MASK) | 4; + atc_reg_write(drd, AUSBC_CFG_USB2PHY_BLK_USB_CTL, reg); + + atc_reg_or(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL, 0xb); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL, 0xFFFFFFFB); + spin(10); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_MISC_TUNE, 0x60000000LL); + + } + is_in_host_mode = true; + + hal_apply_tunables(drd->atc_device, "tunable-host"); + +} +__unused static void atc_bringup(struct drd* drd) { + atc_reg_or(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_SIG, VBUS_DETECT_FORCE_VAL | VBUS_DETECT_FORCE_EN | VBUS_VALID_EXT_FORCE_VAL | VBUS_VALID_EXT_FORCE_EN); + spin(10 * 1000); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL, ~USB2PHY_SIDDQ); + spin(10); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL, ~(USB2PHY_RESET|USB2PHY_PORT_RESET)); + atc_reg_or(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL, USB2PHY_APB_RESETN); + atc_reg_and(drd, AUSBC_CFG_USB2PHY_BLK_USB2PHY_MISC_TUNE, ~(USB2PHY_REFCLK_GATEOFF | USB2PHY_APBCLK_GATEOFF)); + spin(30); +} + +__unused static void enable_endpoint(struct drd* drd, uint8_t index) { + drd_endpoint_start_configuration(drd, index, 0); + drd_endpoint_set_configuration(drd, index, USB_ENDPOINT_CONTROL, EP0_MAX_PACKET_SIZE); + drd_reg_or(drd, G_DALEPENA, 1 << index); +} + +__unused static void drd_bringup(struct drd* drd) { + uint32_t reg; + + pipehandler_reg_and(drd, P_PHY_MUX_SELECT, ~PIPE_CLK_EN); + + pipehandler_reg_and(drd, P_PHY_MUX_SELECT, ~PIPE_MODE); + + reg = pipehandler_reg_read(drd, P_PHY_MUX_SELECT); + reg &= ~PIPE_CLK_EN; + reg |= 0x8; + pipehandler_reg_write(drd, P_PHY_MUX_SELECT, reg); + + reg = pipehandler_reg_read(drd, P_PHY_MUX_SELECT); + reg &= ~PIPE_MODE; + reg |= 0x2; + pipehandler_reg_write(drd, P_PHY_MUX_SELECT, reg); + + reg = pipehandler_reg_read(drd, P_PHY_MUX_SELECT); + reg &= ~PIPE_CLK_EN; + reg |= 0x20; + pipehandler_reg_write(drd, P_PHY_MUX_SELECT, reg); + + pipehandler_reg_and(drd, P_LOCK_PIPE_IF_REQ, ~1); + + + while (pipehandler_reg_read(drd, P_LOCK_PIPE_IF_ACK) & 1) { + ;; + } + + drd_reg_and(drd, G_GUSB2PHYCFG, ~SUSPENDUSB20); + drd_reg_and(drd, G_GUSB3PIPECTL, ~SUSPENDENABLE); + + ausb_reg_and(drd, AUSBC_FORCE_CLK_ON, ~0x1f); + + pipehandler_reg_or(drd, BLK_PIPE_HANDLER_NON_SELECTED_OVERRIDE, DUMMY_PHY_READY); + + pipehandler_reg_and(drd, P_AON_GENERAL_REGS, ~ATC_USB31_DRD_FORCE_CLAMP_EN); + pipehandler_reg_or(drd, P_AON_GENERAL_REGS, ATC_USB31_DRD_SW_VCC_RESET); + + drd_reg_and(drd, G_GUSB2PHYCFG, ~SUSPENDUSB20); + drd_reg_and(drd, G_GUSB3PIPECTL, ~SUSPENDENABLE); + + while (!(configreg_reg_read(drd, USB31DRD_PIPE) & PHY_READY)) { + ;; + } + + drd_reg_write(drd, G_DCTL, DCTL_SOFTRESET); + while (drd_reg_read(drd, G_DCTL) & DCTL_SOFTRESET) { + ;; + } + + if (is_in_host_mode) { + hal_apply_tunables(drd->device, "tunables"); + atc_enable_host(drd, true); + } else { + drd_reg_write(drd, G_GCTL, GCTL_DSBLCLKGTNG | GCTL_PRTCAPDIR(true) | GCTL_PWRDNSCALE(2)); + drd_reg_write(drd, G_DCFG, DCFG_HIGH_SPEED | (8 << 17)); + + drd_reg_write(drd, G_GSBUSCFG0, (1 << 6)); + + drd->virtBaseDMA = alloc_contig(0x4000); + drd->physBaseDMA = vatophys_static((void*)drd->virtBaseDMA); + + uint64_t dartBaseDMA = 0; + size_t dartBaseDMASize = 8; + + if (hal_invoke_service_op(drd->mapper, "dart", DART_BYPASS_CONVERT_PTR, &drd->physBaseDMA, 8, &dartBaseDMA, &dartBaseDMASize)) + panic("failed to translate address to DART address"); + + uint32_t eventc = (drd_reg_read(drd, G_GHWPARAMS(1)) >> 15) & 0x3f; + + for (uint32_t i=0; i < eventc; i++) { + drd_reg_write(drd, G_GEVNTSIZ(i), 0); + } + + drd_reg_write64(drd, G_GEVNTADRLO(0), dartBaseDMA); + drd_reg_write(drd, G_GEVNTADRHI(0), (dartBaseDMA >> 32ULL) & 0xffffffff); + drd_reg_write(drd, G_GEVNTSIZ(0), 0x4000); + drd_reg_write(drd, G_GEVNTCOUNT(0), 0); + + dartBaseDMA += 0x4000; + + drd_device_generic_command(drd, 4, dartBaseDMA & 0xffffffff); + drd_device_generic_command(drd, 5, (dartBaseDMA >> 32ULL) & 0xffffffff); + + + enable_endpoint(drd, ENDPOINT_EP0_OUT); + + drd_reg_or(drd, G_DEVTEN, DEVTEN_USBRSTEVTEN|DEVTEN_DISSCONNEVTEN|DEVTEN_CONNECTDONEEVTEN|DEVTEN_ULSTCNGEN|DEVTEN_WKUPEVTEN|DEVTEN_ERRTICERREVTEN|DEVTEN_VENDEVTSTRCVDEN); + + hal_apply_tunables(drd->device, "tunables"); + + drd_reg_write(drd, G_DCTL, DCTL_RUN_STOP); + } +} + + +static bool register_drd(struct hal_device* device, void* context) { + // DesignWare DWC3 Dual Role Device + + struct drd* drd = context; + + drd->mapper = hal_get_mapper(device, 0); + drd->device = device; + + uint32_t len = 0; + uint32_t* val = dt_prop(device->node, "atc-phy-parent", &len); + + if (val && len >= 4) { + drd->atc_device = hal_get_phandle_device(*val); + } else { + panic("unknown atc-phy-parent!"); + } + + hal_invoke_service_op(drd->mapper->parent, "hal", HAL_DEVICE_CLOCK_GATE_ON, NULL, 0, NULL, NULL); + hal_invoke_service_op(drd->atc_device, "hal", HAL_DEVICE_CLOCK_GATE_ON, NULL, 0, NULL, NULL); + hal_invoke_service_op(drd->device, "hal", HAL_DEVICE_CLOCK_GATE_ON, NULL, 0, NULL, NULL); + + if (hal_invoke_service_op(drd->mapper, "dart", DART_ENTER_BYPASS_MODE, NULL, 0, NULL, NULL)) + panic("drd: could not enter bypass mode"); + + if (strcmp(dt_prop(device->node, "compatible", &len), "usb-drd,t8103") == 0) { + drd->regBase = (uint64_t)hal_map_registers(drd->device, 2, NULL); + drd->configRegBase = (uint64_t)hal_map_registers(drd->device, 1, NULL); + drd->pipeHandlerRegBase = (uint64_t)hal_map_registers(drd->device, 3, NULL); + drd->coreEvtRegBase = (uint64_t)hal_map_registers(drd->device, 4, NULL); + drd->ausbCtlRegBase = (uint64_t)hal_map_registers(drd->device, 6, NULL); + drd->ausbBulkFabricRegBase = (uint64_t)hal_map_registers(drd->device, 7, NULL); + drd->atcLinkRegBase = (uint64_t)hal_map_registers(drd->device, 8, NULL); + } else { + panic("unsupported usb-drd"); + } + + drd->atcRegBase = (uint64_t)hal_map_registers(drd->atc_device, 0, NULL); + drd->atcRegBasePipe = (uint64_t)hal_map_registers(drd->atc_device, 1, NULL); + + drd->irq_task = task_create_extended(&drd->device->name[4], drd_irq_task, TASK_IRQ_HANDLER|TASK_PREEMPT, 0); + + atc_bringup(drd); + if (is_in_host_mode) { + atc_enable_host(drd, true); + } else { + atc_enable_device(drd, true); + } + + drd_bringup(drd); + + USB_DEBUG_PRINT_REGISTERS(drd); + + return true; +} +static int drd_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + if (method == HAL_METASERVICE_START) { + struct drd* drd = svc->context; + + register_drd(device, drd); + + task_bind_to_irq(drd->irq_task, hal_get_irqno(drd->device,0)); + interrupt_associate_context(hal_get_irqno(drd->device,0), drd); + + return 0; + } + return -1; +} + +static bool drd_probe(struct hal_service* svc, struct hal_device* device, void** context) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (node) { + void* val = dt_prop(node, "device_type", &len); + if (val && strcmp(val, "usb-drd") == 0) { + + *context = calloc(sizeof(struct drd), 1); + + return true; + } + } + return false; +} +static struct hal_service drd_svc = { + .name = "drd", + .probe = drd_probe, + .service_op = drd_service_op +}; + +static void drd_init(struct driver* driver) { + hal_register_hal_service(&drd_svc); +} + +REGISTER_DRIVER(drd, drd_init, NULL, 0); diff --git a/src/drivers/usb/synopsys_drd_regs.h b/src/drivers/usb/synopsys_drd_regs.h new file mode 100644 index 00000000..cc3bcc4e --- /dev/null +++ b/src/drivers/usb/synopsys_drd_regs.h @@ -0,0 +1,220 @@ +/* + * pongoOS - https://checkra.in + * + * Copyright (C) 2019-2020 checkra1n team + * Copyright (c) 2016 The Fuchsia Authors + * + * This file is part of pongoOS. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// usb regs from https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/comet-lake-u/intel-400-series-chipset-on-package-platform-controller-hub-register-database/global-core-control-gctl-offset-c110/, , depcmd stuff from https://github.com/manux81/zircon.ext4/blob/8c1cb41b970722711477a5f4c709b9bc91f71bc6/system/dev/usb/dwc3/dwc3-regs.h (fuchsia project, Copyright 2016 The Fuchsia Authors). + +#ifndef SYNOPSYS_DRD_REGS__H_ +#define SYNOPSYS_DRD_REGS__H_ + +#define AUSBC_CFG_USB2PHY_BLK_USB_CTL 0 +#define USB_MODE_MASK 0x7 + +#define AUSBC_CFG_USB2PHY_BLK_USB2PHY_CTL 4 + +#define USB2PHY_RESET 1 +#define USB2PHY_PORT_RESET 2 +#define USB2PHY_APB_RESETN 4 +#define USB2PHY_SIDDQ 8 + +#define BLK_PIPE_HANDLER_NON_SELECTED_OVERRIDE 0x20 +#define DUMMY_PHY_READY 0x8000 + +#define AUSBC_CFG_USB2PHY_BLK_USB2PHY_SIG 8 +#define VBUS_DETECT_FORCE_VAL 1 +#define VBUS_DETECT_FORCE_EN 2 +#define VBUS_VALID_EXT_FORCE_VAL 4 +#define VBUS_VALID_EXT_FORCE_EN 8 + +#define AUSBC_CFG_USB2PHY_BLK_USB2PHY_MISC_TUNE 0x1c +#define USB2PHY_REFCLK_GATEOFF 0x40000000 +#define USB2PHY_APBCLK_GATEOFF 0x20000000 + +#define C0_UTMI_CLK_ACTIVE_EVT_CNT 0x20 + +// AUSBC_CTRLREG_BLK + +#define AUSBC_FORCE_CLK_ON 0xf0 + +// AUSBC_PIPE_HANDLER + +#define P_PHY_MUX_SELECT 0xC // register + +#define PIPE_MODE 3 // bits within P_PHY_MUX_SELECT +#define PIPE_CLK_EN 0x38 // bits within P_PHY_MUX_SELECT + +#define P_LOCK_PIPE_IF_REQ 0x10 +#define P_LOCK_PIPE_IF_ACK 0x14 +#define P_AON_GENERAL_REGS 0x1c +#define ATC_USB31_DRD_FORCE_CLAMP_EN 0x10 // bits within P_AON_GENERAL_REGS +#define ATC_USB31_DRD_SW_VCC_RESET 0x1 // bits within P_AON_GENERAL_REGS +#define USB31DRD_PIPE 0x20 +#define PHY_READY 0x40000000 + +// Device Configuration Register +#define G_DCFG 0x700 + +#define DCFG_DEVSPD 0b111 +#define DCFG_DEVSPD_SHIFT 0 +#define DCFG_FULL_SPEED 0b001 +#define DCFG_HIGH_SPEED 0b000 +#define DCFG_SUPER_SPEED 0b100 +#define DCFG_DEVADDR 0x3F8 +#define DCFG_DEVADDR_SHIFT 3 +#define DCFG_INTRNUM 0x1FC00 +#define DCFG_INTRNUM_SHIFT 10 +#define DCFG_NUMP 0x3E0000 +#define DCFG_NUMP_SHIFT 17 +#define DCFG_LPMCAP 0x400000 + +// Device Control Register +#define G_DCTL 0x704 +#define DCTL_SOFTRESET (1 << 30) +#define DCTL_RUN_STOP (1 << 31) +#define DCTL_CORESOFTRESET (1 << 11) + +// Device Event Enable Register +#define G_DEVTEN 0x708 +#define DEVTEN_VENDEVTSTRCVDEN (1 << 12) +#define DEVTEN_ERRTICERREVTEN (1 << 9) +#define DEVTEN_WKUPEVTEN (1 << 4) +#define DEVTEN_ULSTCNGEN (1 << 3) +#define DEVTEN_CONNECTDONEEVTEN (1 << 2) +#define DEVTEN_USBRSTEVTEN (1 << 1) +#define DEVTEN_DISSCONNEVTEN (1 << 0) + +// Device Status Register +#define G_DSTS 0x70c +#define DSTS_DEVCTRLHLT (1 << 22) + +// Device Generic Command (DGCMD) – Offset c714 +#define G_DGCMDPAR 0x710 +#define G_DGCMD 0x714 +#define DGCMD_CMDACT (1 << 10) +#define DGCMD_CMDMASK 0xf +#define DGCMD_CMDSTSMASK 0xF000 +#define DGCMD_CMDSTSSHIFT 12 + +#define CMDTYP_SET_PERIODIC_REMINDERS 0x02 +#define CMDTYP_SCRATCHPAD_LO 0x04 +#define CMDTYP_SCRATCHPAD_HI 0x05 +#define CMDTYP_TRANSMIT_DEVICE 0x07 +#define CMDTYP_FIFO_FLUSH 0x09 +#define CMDTYP_FIFO_FLUSH_ALL 0x0A +#define CMDTYP_EP_NRDY 0x0C +#define CMDTYP_EP_LOOPBACK_TEST 0x10 + +// Device Active USB Endpoint Enable (DALEPENA) – Offset c720 +#define G_DALEPENA 0x720 + +#define G_GCTL 0x110 +#define GCTL_DSBLCLKGTNG 1 +#define GCTL_PRTCAPDIR(isDevice) ((isDevice ? 0b10 : 0b00) << 12) +#define GCTL_PWRDNSCALE(n) (((n) & 0x1fff) << 19) + +#define G_GPMSTS 0x114 + +#define G_GSTS 0x118 +#define G_GSNPSID 0x120 +#define G_GUSB2PHYCFG 0x200 +#define PHYSOFTRST (1 << 31) + +#define SUSPENDUSB20 0x40 // bits within G_GUSB2PHYCFG +#define G_GUSB3PIPECTL 0x2C0 +#define SUSPENDENABLE 0x20000 + +#define G_GEVNTADRLO(n) (0x400 + 0x10 * (n)) +#define G_GEVNTADRHI(n) (0x404 + 0x10 * (n)) +#define G_GEVNTSIZ(n) (0x408 + 0x10 * (n)) +#define G_GEVNTCOUNT(n) (0x40c + 0x10 * (n)) +#define GEVNTSIZ_EVNTINTRPTMASK (1 << 31) +#define G_GHWPARAMS(n) (0x140 + n * 4) +#define G_GDBGFIFOSPACE 0x160 + +#define G_DEPCMDPAR2(n) (0x800 + 0x10 * (n)) +#define G_DEPCMDPAR1(n) (0x804 + 0x10 * (n)) +#define G_DEPCMDPAR0(n) (0x808 + 0x10 * (n)) +#define G_DEPCMD(n) (0x80c + 0x10 * (n)) + +// Command Types for DEPCMD +#define DEPCFG 1 // Set Endpoint Configuration +#define DEPXFERCFG 2 // Set Endpoint Transfer Resource Configuration +#define DEPGETSTATE 3 // Get EndpointState +#define DEPSSTALL 4 // Set Stall +#define DEPCSTALL 5 // Clear Stall +#define DEPSTRTXFER 6 // Start Transfer +#define DEPUPDXFER 7 // Update Transfer +#define DEPENDXFER 8 // End Transfer +#define DEPSTARTCFG 9 // Start New Configuration + +#define DEPCMD_RESOURCE_INDEX(n) (((n) & 0x7f) << 16) + +// DEPCFG Params 0 +#define DEPCFG_ACTION_INITIALIZE (0 << 30) +#define DEPCFG_ACTION_RESTORE (1 << 30) +#define DEPCFG_ACTION_MODIFY (2 << 30) +#define DEPCFG_BURST_SIZE(n) ((((n) - 1) & 0xf) << 22) +#define DEPCFG_FIFO_NUM(n) (((n) & 0x1f) << 17) +#define DEPCFG_INTERNAL_RETRY (1 << 15) +#define DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3) +#define DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1) + +// DEPCFG Params 1 +#define DEPCFG_FIFO_BASED (1 << 31) +#define DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) +#define DEPCFG_STREAM_CAPABLE (1 << 24) +#define DEPCFG_INTERVAL(n) (((n) & 0xff) << 16) +#define DEPCFG_EBC (1 << 15) // External Buffer Control +#define DEPCFG_EBC_NO_WRITE_BACK (1 << 14) // Don't write back HWO bit to the TRB descriptor +#define DEPCFG_STREAM_EVT_EN (1 << 13) +#define DEPCFG_XFER_NOT_READY_EN (1 << 10) +#define DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) +#define DEPCFG_XFER_COMPLETE_EN (1 << 8) +#define DEPCFG_INTR_NUM(n) (((n) & 0x1f) << 0) + +#define DEPCMD_CMDIOC (1 << 8) // Command Interrupt on Complete +#define DEPCMD_CMDACT (1 << 10) // Command Active + +#define USB_ENDPOINT_CONTROL 0x00 +#define USB_ENDPOINT_ISOCHRONOUS 0x01 +#define USB_ENDPOINT_BULK 0x02 +#define USB_ENDPOINT_INTERRUPT 0x03 + +#define G_BUSERRADDR_LO 0x130 +#define G_BUSERRADDR_HI 0x134 +#define G_GSBUSCFG0 0x100 + + +#endif diff --git a/src/drivers/usb/synopsys_otg.c b/src/drivers/usb/synopsys_otg.c index 5d4c1e49..3497da7a 100644 --- a/src/drivers/usb/synopsys_otg.c +++ b/src/drivers/usb/synopsys_otg.c @@ -1741,23 +1741,31 @@ void usb_main() { static uint64_t reg1=0, reg2=0, reg3=0; void usb_bringup() { - clock_gate(reg1, 0); - clock_gate(reg2, 0); - clock_gate(reg3, 0); + if (reg1) + clock_gate(reg1, 0); + if (reg2) + clock_gate(reg2, 0); + if (reg3) + clock_gate(reg3, 0); spin(1000); - clock_gate(reg1, 1); - clock_gate(reg2, 1); - clock_gate(reg3, 1); + if (reg1) + clock_gate(reg1, 1); + if (reg2) + clock_gate(reg2, 1); + if (reg3) + clock_gate(reg3, 1); // t8011 is really just cursed... - if (socnum == 0x8011) { - *(volatile uint32_t*)(gSynopsysComplexBase + 0x00) = 1; - *(volatile uint32_t*)(gSynopsysComplexBase + 0x24) = 0x3000088; - } else { - *(volatile uint32_t*)(gSynopsysComplexBase + 0x1c) = 0x108; - *(volatile uint32_t*)(gSynopsysComplexBase + 0x5c) = 0x108; + if (gSynopsysComplexBase) { + if (socnum == 0x8011) { + *(volatile uint32_t*)(gSynopsysComplexBase + 0x00) = 1; + *(volatile uint32_t*)(gSynopsysComplexBase + 0x24) = 0x3000088; + } else { + *(volatile uint32_t*)(gSynopsysComplexBase + 0x1c) = 0x108; + *(volatile uint32_t*)(gSynopsysComplexBase + 0x5c) = 0x108; + } } - *(volatile uint32_t *)(gSynopsysOTGBase + 0x8) = dt_get_u32_prop("otgphyctrl", "cfg0-device"); - *(volatile uint32_t *)(gSynopsysOTGBase + 0xc) = dt_get_u32_prop("otgphyctrl", "cfg1-device"); + //*(volatile uint32_t *)(gSynopsysOTGBase + 0x8) = dt_get_u32_prop("otgphyctrl", "cfg0-device"); + //*(volatile uint32_t *)(gSynopsysOTGBase + 0xc) = dt_get_u32_prop("otgphyctrl", "cfg1-device"); *(volatile uint32_t*)(gSynopsysOTGBase) |= 1; spin(20); *(volatile uint32_t*)(gSynopsysOTGBase) &= 0xFFFFFFF3; @@ -1767,8 +1775,9 @@ void usb_bringup() { *(volatile uint32_t*)(gSynopsysOTGBase + 0x4) &= ~2; spin(1500); } +uint32_t otg_irq; -void usb_init() { +void usb_legacy_init() { gSynopsysOTGBase = 0; uint32_t sz = 0; uint64_t *reg = dt_get_prop("otgphyctrl", "reg", &sz); @@ -1792,18 +1801,31 @@ void usb_init() { gSynopsysComplexBase = gIOBase + dt_get_u32_prop("usb-complex", "reg"); // Can't trust "usb-device" dtre entry, because that can be USB3 and we want USB2 gSynopsysBase = (gSynopsysOTGBase & ~0xfffULL) + 0x00100000; - uint32_t otg_irq; - +} + +void usb_init() { struct usb_regs regs; size_t plsz = sizeof(struct usb_regs); if (!hal_get_platform_value("usb_regs", ®s, &plsz)) { - panic("synopsys_otg: need usb_regs platform value!"); + puts("synopsys_otg: need usb_regs platform value! not initializing.."); + return; } - reg1 = gIOBase + regs.reg1; reg2 = gIOBase + regs.reg2; reg3 = gIOBase + regs.reg3; otg_irq = regs.otg_irq; + + bool isUSBDART = false; + struct usb_dart_regs dartregs; + plsz = sizeof(struct usb_dart_regs); + + if (hal_get_platform_value("usb_dart", &dartregs, &plsz)) { + gSynopsysOTGBase = dartregs.synopsysOTGBase; + gSynopsysBase = (gSynopsysOTGBase & ~0xfffULL) + 0x00100000; + isUSBDART = true; + } else { + usb_legacy_init(); + } uint64_t dma_page_v = (uint64_t) alloc_contig(4 * DMA_BUFFER_SIZE); uint64_t dma_page_p = vatophys_static((void*)dma_page_v); @@ -1813,7 +1835,12 @@ void usb_init() { disable_interrupts(); usb_irq_mode = 1; usb_usbtask_handoff_mode = 0; - usb_bringup(); + + if (isUSBDART) { + + } else { + usb_bringup(); + } gSynopsysCoreVersion = reg_read(rGSNPSID) & 0xffff; USB_DEBUG(USB_DEBUG_STANDARD, "gSynopsysCoreVersion: 0x%x", gSynopsysCoreVersion); diff --git a/src/drivers/usb/usb.h b/src/drivers/usb/usb.h index fdce721a..6fad7fdc 100644 --- a/src/drivers/usb/usb.h +++ b/src/drivers/usb/usb.h @@ -44,6 +44,10 @@ struct usb_regs { int otg_irq; }; +struct usb_dart_regs { + uint64_t synopsysOTGBase; +}; + struct setup_packet { uint8_t bmRequestType; uint8_t bRequest; diff --git a/src/dynamic/modload.c b/src/dynamic/modload.c index 1e0bd9bd..863ab7e6 100644 --- a/src/dynamic/modload.c +++ b/src/dynamic/modload.c @@ -105,6 +105,9 @@ struct pongo_exports public_api[] = { EXPORT_SYMBOL(vm_allocate), EXPORT_SYMBOL(strcmp), EXPORT_SYMBOL(queue_rx_string), + EXPORT_SYMBOL(strchr), + EXPORT_SYMBOL(strdup), + EXPORT_SYMBOL(strncmp), EXPORT_SYMBOL(strlen), EXPORT_SYMBOL(strcpy), EXPORT_SYMBOL(task_create), @@ -185,7 +188,9 @@ struct pongo_exports public_api[] = { EXPORT_SYMBOL(phystokv), EXPORT_SYMBOL(free), EXPORT_SYMBOL(hexdump), + EXPORT_SYMBOL(calloc), EXPORT_SYMBOL(memcmp), + EXPORT_SYMBOL(memchr), EXPORT_SYMBOL(map_range), EXPORT_SYMBOL(linear_kvm_alloc), EXPORT_SYMBOL(vm_flush_by_addr_all_asid), @@ -211,9 +216,20 @@ struct pongo_exports public_api[] = { EXPORT_SYMBOL_P(ramdisk_size), EXPORT_SYMBOL_P(autoboot_count), EXPORT_SYMBOL_P(sep_boot_hook), + EXPORT_SYMBOL_P(gFramebuffer), + EXPORT_SYMBOL_P(gWidth), + EXPORT_SYMBOL_P(gHeight), + EXPORT_SYMBOL_P(gRowPixels), EXPORT_SYMBOL_P(aes), EXPORT_SYMBOL_P(_impure_ptr), EXPORT_SYMBOL_P(loader_xfer_recv_size), + EXPORT_SYMBOL_P(gIORVBAR), + EXPORT_SYMBOL_P(gBootJumpToRelocFrom), + EXPORT_SYMBOL_P(gBootJumpToRelocSize), + EXPORT_SYMBOL_P(gBootJumpToReloc), + EXPORT_SYMBOL_P(gBootJumpArgs), + EXPORT_SYMBOL_P(gBootJumpTo), + EXPORT_SYMBOL_P(gBootFlag), {.name = "___stack_chk_guard", .value = &f_stack_chk_guard}, {.name = "___stack_chk_fail", .value = &f_stack_chk_fail}, {.name = "_iprintf", .value = iprintf}, diff --git a/src/kernel/dtree.c b/src/kernel/dtree.c index 3a8f84b4..da6ad982 100644 --- a/src/kernel/dtree.c +++ b/src/kernel/dtree.c @@ -53,12 +53,21 @@ int dt_check(void* mem, uint32_t size, uint32_t* offp) return 0; } -int dt_parse(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, dt_node_t*), void* cbn_arg, int (*cb_prop)(void*, dt_node_t*, int, const char*, void*, uint32_t), void* cbp_arg) + +int dt_parse_ex(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, dt_node_t*), void* cbn_arg, int (*cb_prop)(void*, dt_node_t*, int, const char*, void*, uint32_t), void* cbp_arg, uint32_t flags) { if (cb_node) { - int r = cb_node(cbn_arg, node); - if (r != 0) - return r; + if (flags & 1) { // only enumerate children in cb_node + if (depth == 1) { + int r = cb_node(cbn_arg, node); + if (r != 0) + return r; + } + } else { + int r = cb_node(cbn_arg, node); + if (r != 0) + return r; + } } if (depth >= 0 || cb_prop) { uint32_t off = sizeof(dt_node_t); @@ -75,7 +84,7 @@ int dt_parse(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, d if (depth >= 0) { for (uint32_t i = 0, max = node->nchld; i < max; ++i) { uint32_t add = 0; - int r = dt_parse((dt_node_t*)((uintptr_t)node + off), depth + 1, &add, cb_node, cbn_arg, cb_prop, cbp_arg); + int r = dt_parse_ex((dt_node_t*)((uintptr_t)node + off), depth + 1, &add, cb_node, cbn_arg, cb_prop, cbp_arg, flags); if (r != 0) return r; off += add; @@ -86,7 +95,10 @@ int dt_parse(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, d } return 0; } - +int dt_parse(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, dt_node_t*), void* cbn_arg, int (*cb_prop)(void*, dt_node_t*, int, const char*, void*, uint32_t), void* cbp_arg) +{ + return dt_parse_ex(node,depth,offp,cb_node,cbn_arg,cb_prop,cbp_arg,0); +} static int dt_find_cb(void* a, dt_node_t* node, int depth, const char* key, void* val, uint32_t len) { dt_find_cb_t* arg = a; diff --git a/src/kernel/entry.c b/src/kernel/entry.c index 2f84df8d..18009983 100644 --- a/src/kernel/entry.c +++ b/src/kernel/entry.c @@ -119,6 +119,7 @@ char pongo_sched_tick() { char soc_name[9] = {}; uint32_t socnum = 0x0; void (*sep_boot_hook)(void); +extern void quiesce_core(); __attribute__((noinline)) void pongo_entry_cached() { @@ -141,6 +142,7 @@ __attribute__((noinline)) void pongo_entry_cached() else if(strcmp(soc_name, "t8011") == 0) socnum = 0x8011; else if(strcmp(soc_name, "t8012") == 0) socnum = 0x8012; else if(strcmp(soc_name, "t8015") == 0) socnum = 0x8015; + else if(strcmp(soc_name, "t8103") == 0) socnum = 0x8103; else if(strcmp(soc_name, "s8000") == 0) { const char *sgx = dt_get_prop("sgx", "compatible", NULL); @@ -225,7 +227,7 @@ __attribute__((noinline)) void pongo_entry_cached() // current task was not to be scheduled or we got volountarily yielded } if (should_wfe) { - __asm__("wfe"); + quiesce_core(); } } timer_disable(); @@ -235,6 +237,17 @@ __attribute__((noinline)) void pongo_entry_cached() while (gBootFlag) { + if (gBootFlag == BOOT_FLAG_JUMP) { + if (gBootJumpToReloc) { + screen_puts("relocating..."); + extern volatile void smemcpy128(void*,void*,uint32_t); + smemcpy128 ((void*)gBootJumpToReloc, (void*)gBootJumpToRelocFrom, gBootJumpToRelocSize/16); + screen_puts("done relocating"); + } + + screen_puts("Jumping to image..."); + return; + } if (gBootFlag == BOOT_FLAG_RAW) { screen_fill_basecolor(); return; @@ -267,9 +280,28 @@ __attribute__((noinline)) void pongo_entry_cached() */ volatile void jump_to_image_extended(uint64_t image, uint64_t args, uint64_t original_image); extern uint64_t gPongoSlide; +uint64_t gIORVBAR; +uint64_t gBootJumpTo; +uint64_t gBootJumpArgs[4]; + +uint64_t gBootJumpToReloc; +uint64_t gBootJumpToRelocFrom; +uint64_t gBootJumpToRelocSize; void pongo_entry(uint64_t *kernel_args, void *entryp, void (*exit_to_el1_image)(void *boot_args, void *boot_entry_point)) { + extern void iorvbar_table(); + gIORVBAR = (uint64_t) iorvbar_table; + + uint64_t hcr_el2 = 0, hcr_el2_orig = 0; + if (get_el() == 2) { + asm volatile("mrs %0, hcr_el2" : "=r"(hcr_el2)); + hcr_el2_orig = hcr_el2; + hcr_el2 |= (1ULL << 27); // VHE + hcr_el2 |= (1ULL << 34); // E2H + asm volatile("msr hcr_el2, %0" : : "r"(hcr_el2)); + } + gBootArgs = (boot_args*)kernel_args; gEntryPoint = entryp; lowlevel_setup(gBootArgs->physBase & 0xFFFFFFFF, gBootArgs->memSize); @@ -283,6 +315,11 @@ void pongo_entry(uint64_t *kernel_args, void *entryp, void (*exit_to_el1_image)( set_exception_stack_core0(); gFramebuffer = (uint32_t*)gBootArgs->Video.v_baseAddr; lowlevel_cleanup(); + + if (get_el() == 2) { + asm volatile("msr hcr_el2, %0" : : "r"(hcr_el2_orig)); + } + if(gBootFlag == BOOT_FLAG_RAW) { jump_to_image_extended(((uint64_t)loader_xfer_recv_data) - kCacheableView + 0x800000000, (uint64_t)gBootArgs, (uint64_t)gEntryPoint); @@ -291,6 +328,10 @@ void pongo_entry(uint64_t *kernel_args, void *entryp, void (*exit_to_el1_image)( { linux_boot(); } + else if(gBootFlag == BOOT_FLAG_JUMP) + { + ((void (*)(uint64_t,uint64_t,uint64_t,uint64_t))gBootJumpTo)(gBootJumpArgs[0], gBootJumpArgs[1], gBootJumpArgs[2], gBootJumpArgs[3]); + } else { tz_lockdown(); diff --git a/src/kernel/int.S b/src/kernel/int.S index 91f90a38..60981e91 100644 --- a/src/kernel/int.S +++ b/src/kernel/int.S @@ -158,6 +158,139 @@ _exception_vector: b . /* SError/vSError */ +.globl _exception_vector_el2 +_exception_vector_el2: + /* Current EL with SP0, handle within SP0 */ + msr spsel, #1 + sub sp, sp, #0x340 + stp x0,x1,[sp] + msr spsel, #0 + mov x1, sp + msr spsel, #1 + adr x0, Jsync_exc + b exc_handler /* Synchronous */ +.balign 128 + msr spsel, #1 + sub sp, sp, #0x340 + stp x0,x1,[sp] + msr spsel, #0 + mov x1, sp + msr spsel, #1 + adr x0, Jirq_exc + b exc_handler /* IRQ/vIRQ */ +.balign 128 + msr spsel, #1 + sub sp, sp, #0x340 + stp x0,x1,[sp] + msr spsel, #0 + mov x1, sp + msr spsel, #1 + adr x0, Jfiq_exc + b exc_handler /* FIQ/vFIQ */ +.balign 128 + msr spsel, #1 + sub sp, sp, #0x340 + stp x0,x1,[sp] + msr spsel, #0 + mov x1, sp + msr spsel, #1 + adr x0, Jserror_exc + b exc_handler /* SError/vSError */ +.balign 128 + + /* Current EL with SPn */ + sub sp, sp, #0x340 + stp x0,x1,[sp] + adr x0, Jsync_exc + add x1, sp, #0x340 + b exc_handler /* Synchronous */ +.balign 128 + sub sp, sp, #0x340 + stp x0,x1,[sp] + adr x0, Jirq_exc_el1sp1 + add x1, sp, #0x340 + b exc_handler /* IRQ/vIRQ */ +.balign 128 + sub sp, sp, #0x340 + stp x0,x1,[sp] + adr x0, Jfiq_exc_el1sp1 + add x1, sp, #0x340 + b exc_handler /* FIQ/vFIQ */ +.balign 128 + sub sp, sp, #0x340 + stp x0,x1,[sp] + adr x0, Jserror_exc + add x1, sp, #0x340 + b exc_handler /* SError/vSError */ +.balign 128 + + /* Lower EL with Aarch64 */ + msr spsel, #1 + str x0, [sp, #-0x10] + mrs x0, tpidr_el2 + ldr x0, [x0, #0x210] // struct task -> kernel_stack + str x1, [x0, #8] + ldr x1, [sp, #-0x10] + str x1, [x0] + msr spsel, #0 + mov x1, sp + mov sp, x0 + adr x0, Jsync_exc_el0 + b exc_handler /* Synchronous */ +.balign 128 + msr spsel, #1 + str x0, [sp, #-0x10] + mrs x0, tpidr_el2 + ldr x0, [x0, #0x210] // struct task -> kernel_stack + str x1, [x0, #8] + ldr x1, [sp, #-0x10] + str x1, [x0] + msr spsel, #0 + mov x1, sp + mov sp, x0 + adr x0, Jirq_exc + b exc_handler /* IRQ/vIRQ */ +.balign 128 + msr spsel, #1 + str x0, [sp, #-0x10] + mrs x0, tpidr_el2 + ldr x0, [x0, #0x210] // struct task -> kernel_stack + str x1, [x0, #8] + ldr x1, [sp, #-0x10] + str x1, [x0] + msr spsel, #0 + mov x1, sp + mov sp, x0 + adr x0, Jfiq_exc + b exc_handler /* FIQ/vFIQ */ +.balign 128 + msr spsel, #1 + str x0, [sp, #-0x10] + mrs x0, tpidr_el2 + ldr x0, [x0, #0x210] // struct task -> kernel_stack + str x1, [x0, #8] + ldr x1, [sp, #-0x10] + str x1, [x0] + msr spsel, #0 + mov x1, sp + msr spsel, #1 + mov sp, x0 + adr x0, Jserror_exc + b exc_handler /* SError/vSError */ +.balign 128 + + /* Lower EL with Aarch32 */ + +.balign 128 + b . /* Synchronous */ +.balign 128 + b . /* IRQ/vIRQ */ +.balign 128 + b . /* FIQ/vFIQ */ +.balign 128 + b . /* SError/vSError */ + + Jsync_exc: b _sync_exc Jserror_exc: @@ -219,30 +352,64 @@ exc_handler: str d30, [sp,#0x310] str d31, [sp,#0x320] - mrs x29, esr_el1 - stp x30,x29,[sp,#0xf0] - mrs x0, elr_el1 - mrs x1, far_el1 - stp x0,x1,[sp,#0x100] - mrs x1, spsr_el1 - str x1, [sp, #0x110] + mrs x0, currentel + cmp x0, #0x8 + b.eq 2f - // Need to keep x29 valid for panic() - add x29, sp, #0xe8 - mov x0, sp - blr x16 +mrs x29, esr_el1 +stp x30,x29,[sp,#0xf0] +mrs x0, elr_el1 +mrs x1, far_el1 +stp x0,x1,[sp,#0x100] +mrs x1, spsr_el1 +str x1, [sp, #0x110] - cmp x0, #1 - b.ne exc_done - bl _task_yield_preemption +// Need to keep x29 valid for panic() +add x29, sp, #0xe8 +mov x0, sp +blr x16 -exc_done: - ldp x0,x1,[sp,#0x100] - msr elr_el1, x0 - msr far_el1, x1 +cmp x0, #1 +b.ne 6f +bl _task_yield_preemption + +6: +ldp x0,x1,[sp,#0x100] +msr elr_el1, x0 +msr far_el1, x1 + +ldr x1, [sp, #0x110] +msr spsr_el1, x1 + + + b 1f +2: - ldr x1, [sp, #0x110] - msr spsr_el1, x1 +mrs x29, esr_el2 +stp x30,x29,[sp,#0xf0] +mrs x0, elr_el2 +mrs x1, far_el2 +stp x0,x1,[sp,#0x100] +mrs x1, spsr_el2 +str x1, [sp, #0x110] + +// Need to keep x29 valid for panic() +add x29, sp, #0xe8 +mov x0, sp +blr x16 + +cmp x0, #1 +b.ne 6f +bl _task_yield_preemption + +6: +ldp x0,x1,[sp,#0x100] +msr elr_el2, x0 +msr far_el2, x1 + +ldr x1, [sp, #0x110] +msr spsr_el2, x1 +1: mov x0, sp @@ -310,30 +477,67 @@ exc_done: .globl __task_switch_asserted .globl _task_load .globl _task_load_asserted + _task_current: + mrs x0, currentel + cmp x0, #0x8 + b.eq task_current_el2 mrs x0, tpidr_el1 ret + +task_current_el2: + mrs x0, tpidr_el2 + ret + + + __task_set_current: + mrs x16, currentel + cmp x16, #0x8 + b.eq task_set_current_el2 + msr tpidr_el1, x0 ret +task_set_current_el2: + msr tpidr_el2, x0 + ret + + __task_switch: mov x16, x0 - mov x17, x1 + mov x17, x30 bl _disable_interrupts - mrs x2, tpidr_el1 + bl _task_current + mov x2, x0 mov x0, x16 - mov x1, x17 + mov x30, x17 __task_switch_asserted: isb dmb sy + + mrs x2, currentel + cmp x2, #0x8 + b.eq 2f mrs x2, tpidr_el1 + b 1f +2: + mrs x2, tpidr_el2 +1: + stp x2, x3, [x2,#0x10] stp x4, x5, [x2,#0x20] stp x6, x7, [x2,#0x30] stp x8, x9, [x2,#0x40] + mrs x1, currentel + cmp x1, #0x8 + b.eq 2f mrs x1, ttbr1_el1 + b 1f +2: + mrs x1, ttbr1_el2 +1: str x1, [x2, #0x1C8] mrs x1, spsel str w1, [x2, #0x1C0] @@ -360,17 +564,46 @@ __task_switch_asserted: stp d10, d11, [x2,#0x140] stp d12, d13, [x2,#0x160] stp d14, d15, [x2,#0x180] + + mrs x16, currentel + cmp x16, #0x8 + b.eq 2f msr tpidr_el1, x0 + b 1f +2: + msr tpidr_el2, x0 +1: msr spsel, #1 ldr x8, [x0, #0x1B8] mov sp, x8 msr spsel, #0 + + + mrs x2, currentel + cmp x2, #0x8 + b.eq 2f mrs x2, ttbr1_el1 + b 1f +2: + mrs x2, ttbr1_el2 +1: + + ldr x1, [x0, #0x1C8] cmp x2, x1 isb + + mrs x16, currentel + cmp x16, #0x8 + b.eq 2f msr ttbr1_el1, x1 + b 1f +2: + msr ttbr1_el2, x1 +1: + + dsb sy ldp x2, x3, [x0,#0x10] ldp x4, x5, [x0,#0x20] @@ -379,7 +612,6 @@ __task_switch_asserted: ldp x10, x11, [x0,#0x50] ldp x12, x13, [x0,#0x60] ldp x14, x15, [x0,#0x70] - ldp x16, x17, [x0,#0x80] ldp x18, x19, [x0,#0x90] ldp x20, x21, [x0,#0xa0] ldp x22, x23, [x0,#0xb0] @@ -393,7 +625,17 @@ __task_switch_asserted: ldp d12, d13, [x0,#0x160] ldp d14, d15, [x0,#0x180] + mrs x16, currentel + cmp x16, #0x8 + b.eq 2f msr tpidr_el1, x0 + b 1f +2: + msr tpidr_el2, x0 +1: + + ldp x16, x17, [x0,#0x80] + mov sp, x1 ldr x1, [x0, #0x100] add x1, x1, #1 @@ -408,13 +650,21 @@ _task_load: mov x16, x0 mov x17, x1 bl _disable_interrupts - mrs x2, tpidr_el1 + bl _task_current + mov x2, x0 mov x0, x16 mov x1, x17 b _task_load_asserted _task_load_asserted: + mrs x16, currentel + cmp x16, #0x8 + b.eq 2f msr tpidr_el1, x0 + b 1f +2: + msr tpidr_el2, x0 +1: msr spsel, #1 ldr x8, [x0, #0x1B8] @@ -423,7 +673,15 @@ _task_load_asserted: ldr x1, [x0, #0x1C8] isb + mrs x16, currentel + cmp x16, #0x8 + b.eq 2f msr ttbr1_el1, x1 + b 1f +2: + msr ttbr1_el2, x1 +1: + dsb sy ldp x2, x3, [x0,#0x10] @@ -433,7 +691,6 @@ _task_load_asserted: ldp x10, x11, [x0,#0x50] ldp x12, x13, [x0,#0x60] ldp x14, x15, [x0,#0x70] - ldp x16, x17, [x0,#0x80] ldp x18, x19, [x0,#0x90] ldp x20, x21, [x0,#0xa0] @@ -448,7 +705,18 @@ _task_load_asserted: ldp d12, d13, [x0,#0x160] ldp d14, d15, [x0,#0x180] + mrs x16, currentel + cmp x16, #0x8 + b.eq 2f msr tpidr_el1, x0 + b 1f +2: + msr tpidr_el2, x0 +1: + + ldp x16, x17, [x0,#0x80] + + mov sp, x1 ldr x1, [x0, #0x100] add x1, x1, #1 @@ -462,12 +730,21 @@ _task_load_asserted: .globl _task_entry_j _task_entry_j: + mrs x16, currentel + cmp x16, #0x8 + b.eq 2f msr elr_el1, x0 msr spsr_el1, x3 + mrs x0, tpidr_el1 + b 1f +2: + msr elr_el2, x0 + msr spsr_el2, x3 + mrs x0, tpidr_el2 +1: mov sp, x1 mov lr, x2 mov x29, xzr - mrs x0, tpidr_el1 add x0, x0, #0x220 // -> struct task initial_state ldp x2, x3, [x0,#0x10] @@ -504,59 +781,49 @@ _set_timer_ctr: isb ret -.globl _get_spsr_el1 -_get_spsr_el1: - mrs x0, spsr_el1 - ret -.globl _set_spsr_el1 -_set_spsr_el1: - msr spsr_el1, x0 - ret -#if 0 -.globl _set_l2c_err_sts -.globl _get_l2c_err_sts -.globl _set_l2c_err_adr -.globl _get_l2c_err_adr -.globl _set_l2c_err_inf -.globl _get_l2c_err_inf -.globl _set_lsu_err_sts -.globl _get_lsu_err_sts - -// TODO: this is whack, and some were probably copied wrong... -_set_l2c_err_sts: - .long 0xd51bf800 - isb sy - ret -_get_l2c_err_sts: - .long 0xd53bf800 - isb sy - ret -_set_l2c_err_adr: - .long 0xd51bf000 - isb sy - ret -_get_l2c_err_adr: - .long 0xd53bf900 - isb sy - ret -_set_l2c_err_inf: - .long 0xd51bf000 - isb sy - ret -_get_l2c_err_inf: - .long 0xd53bf900 - isb sy - ret -set_lsu_err_sts: - .long 0xd51bf000 - isb sy - ret -get_lsu_err_sts: - .long 0xd53bf000 - isb sy +.global _quiesce_core +_quiesce_core: + sub sp, sp, #0x120 + + stp x10, x11, [sp] + stp x12, x13, [sp,#0x10] + stp x14, x15, [sp,#0x20] + + stp x18, x19, [sp,#0x30] + stp x20, x21, [sp,#0x40] + stp x22, x23, [sp,#0x50] + stp x24, x25, [sp,#0x60] + stp x26, x27, [sp,#0x70] + stp x28, x29, [sp,#0x80] + stp x30, x1, [sp, #0x90] + + stp d8, d9, [sp,#0xa0] + stp d10, d11, [sp,#0xc0] + stp d12, d13, [sp,#0xe0] + stp d14, d15, [sp,#0x100] + + wfi + cmp x30, xzr + b.ne 1f // no need to restore registers since we didn't lose state. + + + ldp x10, x11, [sp] + ldp x12, x13, [sp,#0x10] + ldp x14, x15, [sp,#0x20] + + ldp x18, x19, [sp,#0x30] + ldp x20, x21, [sp,#0x40] + ldp x22, x23, [sp,#0x50] + ldp x24, x25, [sp,#0x60] + ldp x26, x27, [sp,#0x70] + ldp x28, x29, [sp,#0x80] + ldp x30, x1, [sp, #0x90] + + ldp d8, d9, [sp,#0xa0] + ldp d10, d11, [sp,#0xc0] + ldp d12, d13, [sp,#0xe0] + ldp d14, d15, [sp,#0x100] + +1: + add sp, sp, #0x120 ret -.align 3 -spsel_err: -.ascii "task_switch/load called on exception stack" -.align 3 -#endif diff --git a/src/kernel/lowlevel.c b/src/kernel/lowlevel.c index f4b76077..81904900 100644 --- a/src/kernel/lowlevel.c +++ b/src/kernel/lowlevel.c @@ -33,6 +33,7 @@ __asm__( ".globl _get_el\n" ".globl _rebase_pc\n" ".globl _set_vbar_el1\n" + ".globl _set_vbar_el2\n" ".globl __enable_interrupts\n" ".globl __disable_interrupts\n" ".globl _get_mpidr\n" @@ -42,6 +43,8 @@ __asm__( ".globl _invalidate_icache\n" ".globl _enable_mmu_el1\n" ".globl _disable_mmu_el1\n" + ".globl _enable_mmu_el2\n" + ".globl _disable_mmu_el2\n" ".globl _get_ticks\n" ".globl _panic_new_fp\n" ".globl _copy_safe_internal\n" @@ -63,7 +66,10 @@ __asm__( " msr vbar_el1, x0\n" " isb\n" " ret\n" - + "_set_vbar_el2:\n" + " msr vbar_el2, x0\n" + " isb\n" + " ret\n" "__enable_interrupts:\n" " msr daifclr,#0xf\n" " isb\n" @@ -114,6 +120,29 @@ __asm__( " isb sy\n" " ret\n" + ".arch v8.4a\n" + "_enable_mmu_el2:\n" + " dsb sy\n" + " msr mair_el2, x2\n" + " msr tcr_el2, x1\n" + " msr ttbr0_el2, x0\n" + " msr ttbr1_el2, x3\n" + " isb sy\n" + " tlbi alle2\n" + " isb sy\n" + " ic iallu\n" + " isb sy\n" + " mrs x3, sctlr_el2\n" + " orr x3, x3, #1\n" + " orr x3, x3, #4\n" + " orr x3, x3, #0x800000\n" // enable SPAN if possible + " and x3, x3, #(~2)\n" + " msr sctlr_el2, x3\n" + " ic iallu\n" + " dsb sy\n" + " isb sy\n" + " ret\n" + "_disable_mmu_el1:\n" " dsb sy\n" " isb sy\n" @@ -127,6 +156,19 @@ __asm__( " isb sy\n" " ret\n" + "_disable_mmu_el2:\n" + " dsb sy\n" + " isb sy\n" + " mrs x3, sctlr_el2\n" + " and x3, x3, #(~1)\n" + " and x3, x3, #(~4)\n" + " msr sctlr_el2, x3\n" + " tlbi alle2\n" + " ic iallu\n" + " dsb sy\n" + " isb sy\n" + " ret\n" + "_get_ticks:\n" " isb sy\n" " mrs x0, cntpct_el0\n" @@ -333,9 +375,11 @@ int sync_exc_el0(uint64_t* state) { dis_int_count = 0; return sync_exc(state); } + uint32_t interrupt_vector() { return (*(volatile uint32_t *)(gInterruptBase + 0x2004)); } + uint64_t interruptCount = 0, fiqCount = 0; uint32_t do_preempt = 1; void disable_preemption() { @@ -358,7 +402,7 @@ int irq_exc() { } uint32_t intr = interrupt_vector(); while (intr) { - task_irq_dispatch(intr); + task_irq_dispatch(intr & 0xfff); intr = interrupt_vector(); } if (dis_int_count != 1) panic("IRQ handler left interrupts disabled..."); @@ -434,8 +478,10 @@ void sleep(uint32_t sec) { usleep((uint64_t)sec * 1000000ULL); } -int gAICVersion = -1; -int gAICStyle = -1; + +struct task** irqvecs; +void** irqctx; +uint32_t irq_count; __attribute__((used)) static void interrupt_or_config(uint32_t bits) { *(volatile uint32_t*)(gInterruptBase + 0x10) |= bits; @@ -444,13 +490,28 @@ __attribute__((used)) static void interrupt_and_config(uint32_t bits) { *(volatile uint32_t*)(gInterruptBase + 0x10) &= bits; } uint32_t interrupt_masking_base = 0; +void set_interrupt_affinity(uint32_t reg, uint32_t cpus) { + if (reg > irq_count) { + panic("set_interrupt_affinity: irqno out of bounds (%d > %d)", reg, irq_count); + } + (*(volatile uint32_t *)(gInterruptBase + 0x3000 + reg * 4)) = 1 << cpus; +} void unmask_interrupt(uint32_t reg) { + if (reg > irq_count) { + panic("unmask_interrupt: irqno out of bounds (%d > %d)", reg, irq_count); + } + set_interrupt_affinity(reg, 0); (*(volatile uint32_t *)(gInterruptBase + 0x4180 + ((reg >> 5) * 4))) = (1 << ((reg) & 0x1F)); - } void mask_interrupt(uint32_t reg) { + if (reg > irq_count) { + panic("mask_interrupt: irqno out of bounds (%d > %d)", reg, irq_count); + } (*(volatile uint32_t *)(gInterruptBase + 0x4100 + ((reg >> 3) * 4))) = (1 << ((reg) & 0x1F)); } +uint32_t count_interrupts() { + return *(volatile uint32_t *)(gInterruptBase + 4); +} #define WDT_CHIP_TMR (*(volatile uint32_t*)(gWDTBase + 0x0)) #define WDT_CHIP_RST (*(volatile uint32_t*)(gWDTBase + 0x4)) @@ -512,9 +573,9 @@ typedef struct typedef struct { - uint32_t flg : 8, + uint32_t flg : 8, a : 16, - id : 8; + id1 : 8; uint32_t b; uint32_t c : 16, idx : 8, @@ -522,7 +583,8 @@ typedef struct uint32_t d; uint32_t e; uint32_t f; - uint32_t g; + uint32_t g : 16, + id2 : 16; uint32_t h; char name[0x10]; } pmgr_dev_t; @@ -534,44 +596,160 @@ static pmgr_reg_t *gPMGRreg = NULL; static pmgr_map_t *gPMGRmap = NULL; static pmgr_dev_t *gPMGRdev = NULL; -void pmgr_init() -{ - dt_node_t *pmgr = dt_find(gDeviceTree, "pmgr"); - gPMGRreg = dt_prop(pmgr, "reg", &gPMGRreglen); - gPMGRmap = dt_prop(pmgr, "ps-regs", &gPMGRmaplen); - gPMGRdev = dt_prop(pmgr, "devices", &gPMGRdevlen); - gPMGRreglen /= sizeof(*gPMGRreg); - gPMGRmaplen /= sizeof(*gPMGRmap); - gPMGRdevlen /= sizeof(*gPMGRdev); - gPMGRBase = gIOBase + gPMGRreg[0].addr; - gWDTBase = gIOBase + dt_get_u64_prop("wdt", "reg"); - command_register("reset", "resets the device", wdt_reset); - command_register("crash", "branches to an invalid address", (void*)0x41414141); -} -void interrupt_init() { - gInterruptBase = dt_get_u32_prop("aic", "reg"); - gInterruptBase += gIOBase; - - gAICVersion = dt_get_u32_prop("aic", "aic-version"); - - interrupt_or_config(0xE0000000); - interrupt_or_config(1); // enable interrupt -} + void interrupt_teardown() { wdt_disable(); task_irq_teardown(); } +void interrupt_associate_context(uint32_t irqno, void* context) { + if (irqno > irq_count) { + panic("interrupt_associate_context: irqno out of bounds (%d > %d)", irqno, irq_count); + } + + irqctx[irqno] = context; +} +void* interrupt_context(uint32_t irqno) { + if (irqno > irq_count) { + panic("interrupt_context: irqno out of bounds (%d > %d)", irqno, irq_count); + } + + return irqctx[irqno]; +} + +struct hal_device* gInterruptDevice; + +static bool lowlevel_probe(struct hal_service* svc, struct hal_device* device, void** context) { + uint32_t len = 0; + dt_node_t* node = device->node; + if (node) { + void* val = dt_prop(node, "name", &len); + if (val && strcmp(val, "aic") == 0) { + if (gInterruptBase) panic("multiple aic probes! unsupported."); + + gInterruptDevice = device; + + gInterruptBase = (uint64_t) hal_map_registers(device, 0, NULL); + + irq_count = count_interrupts(); + irqvecs = calloc(irq_count, sizeof(struct task*)); + irqctx = calloc(irq_count, sizeof(void*)); + + interrupt_or_config(0xE0000000); + interrupt_or_config(1); // enable interrupt + + return true; + } else + if (val && strcmp(val, "pmgr") == 0) { // move this to its own driver + if (gPMGRreg) panic("multiple pmgr probes! unsupported."); + + void* pmreg = dt_prop(device->node, "reg", &gPMGRreglen); + gPMGRreg = malloc(gPMGRreglen); + memcpy(gPMGRreg, pmreg, gPMGRreglen); + + gPMGRmap = dt_prop(device->node, "ps-regs", &gPMGRmaplen); + gPMGRdev = dt_prop(device->node, "devices", &gPMGRdevlen); + gPMGRreglen /= sizeof(*gPMGRreg); + gPMGRmaplen /= sizeof(*gPMGRmap); + gPMGRdevlen /= sizeof(*gPMGRdev); + + for (int i=0; i < gPMGRreglen; i++) { + gPMGRreg[i].addr = (uint64_t) hal_map_physical_mmio(gPMGRreg[i].addr, gPMGRreg[i].size); + } + gPMGRBase = gPMGRreg[0].addr; + + return false; + } else + if (val && strcmp(val, "wdt") == 0) { // move this to its own driver + if (gWDTBase) panic("multiple wdt probes! unsupported."); + + gWDTBase = (uint64_t) hal_map_registers(device, 0, NULL); + command_register("reset", "resets the device", wdt_reset); + command_register("crash", "branches to an invalid address", (void*)0x41414141); + return false; + } + } + return false; +} + +static int lowlevel_service_op(struct hal_device_service* svc, struct hal_device* device, uint32_t method, void* data_in, size_t data_in_size, void* data_out, size_t *data_out_size) { + if (method == IRQ_MASK && data_in_size == 4) { + mask_interrupt(*(uint32_t*)data_in); + return 0; + } else if (method == IRQ_UNMASK && data_in_size == 4) { + unmask_interrupt(*(uint32_t*)data_in); + return 0; + } else if (method == IRQ_ACK) { + return 0; + } else if (method == IRQ_REGISTER && data_in_size == sizeof(struct irq_register_args)) { + struct irq_register_args* args = data_in; + uint32_t irq_number = args->irq; + interrupt_associate_context(irq_number, args->context); + task_bind_to_irq(args->task, irq_number); + return 0; + } + return -1; +} + +static struct hal_service lowlevel_svc = { + .name = "irqctrl", + .probe = lowlevel_probe, + .service_op = lowlevel_service_op, + .flags = SERVICE_FLAGS_EARLY_PROBE +}; + +static void lowlevel_init(struct driver* driver) { + hal_register_hal_service(&lowlevel_svc); +} + +REGISTER_DRIVER(lowlevel, lowlevel_init, NULL, 0); + +const char* device_clock_name_by_id(uint32_t idx) +{ + for(uint32_t i = 0; i < gPMGRdevlen; ++i) + { + pmgr_dev_t *d = &gPMGRdev[i]; + uint32_t cid = 0; + + if (d->id1) { + cid = d->id1; + } else { + cid = d->id2; + } + if(cid != idx) + { + continue; + } + + return d->name; + } + return NULL; +} + uint64_t device_clock_by_id(uint32_t id) { for(uint32_t i = 0; i < gPMGRdevlen; ++i) { pmgr_dev_t *d = &gPMGRdev[i]; - if(d->id != id) + uint32_t cid = 0; + + if (d->id1) { + cid = d->id1; + } else { + cid = d->id2; + } + if(cid != id) { continue; } - if((d->flg & 0x10) || d->map >= gPMGRmaplen) + + if(d->flg & 0x10) { + if (d->b) { + return device_clock_by_id(d->b); + } + break; + } + if (d->map >= gPMGRmaplen) { break; } @@ -581,7 +759,7 @@ uint64_t device_clock_by_id(uint32_t id) { break; } - return gIOBase + r->addr + m->off + (d->idx << 3); + return r->addr + m->off + (d->idx << 3); } return 0; } @@ -595,7 +773,13 @@ uint64_t device_clock_by_name(const char *name) { continue; } - if((d->flg & 0x10) || d->map >= gPMGRmaplen) + if(d->flg & 0x10) { + if (d->b) { + return device_clock_by_id(d->b); + } + break; + } + if(d->map >= gPMGRmaplen) { break; } @@ -605,7 +789,7 @@ uint64_t device_clock_by_name(const char *name) { break; } - return gIOBase + r->addr + m->off + (d->idx << 3); + return r->addr + m->off + (d->idx << 3); } return 0; } diff --git a/src/kernel/main_task.c b/src/kernel/main_task.c index dc6d56b7..e044da99 100644 --- a/src/kernel/main_task.c +++ b/src/kernel/main_task.c @@ -45,10 +45,14 @@ void shell_main(); uint64_t gBootTimeTicks; void pongo_main_task() { + if (!socnum) { + iprintf("unknown platform: %s, will likely crash soon...\n", soc_name); + } + gBootTimeTicks = get_ticks(); - - // Setup GPIO Base - gpio_early_init(); + + // Setup HAL + hal_init(); // Setup serial pinmux serial_pinmux_init(); @@ -56,18 +60,9 @@ void pongo_main_task() { // Enable serial TX serial_early_init(); - // Setup HAL - hal_init(); - - // Turn on IRQ controller - interrupt_init(); - // Enable IRQ serial RX serial_init(); - // Initialize pmgr - pmgr_init(); - /* Initialize display */ @@ -84,17 +79,17 @@ void pongo_main_task() { // Set up AES aes_init(); - + //fb_reset_cursor(); puts(""); puts("#=================="); puts("#"); - puts("# pongoOS " PONGO_VERSION); + iprintf("# pongoOS " PONGO_VERSION " (EL%d)\n", get_el()); puts("#"); puts("# https://checkra.in"); puts("#"); puts("#=================="); screen_mark_banner(); - + iprintf("Booted by: %s\n", dt_get_prop("chosen", "firmware-version", NULL)); strcpy(dt_get_prop("chosen", "firmware-version", NULL), "pongoOS-"); strcat(dt_get_prop("chosen", "firmware-version", NULL), PONGO_VERSION); @@ -105,5 +100,10 @@ void pongo_main_task() { #endif iprintf("Running on: %s\n", hal_platform_name()); + // start HAL device services + extern void hal_issue_recursive_start(); + + hal_issue_recursive_start(); + shell_main(); } diff --git a/src/kernel/mm.c b/src/kernel/mm.c index 7c24286c..4db1945d 100644 --- a/src/kernel/mm.c +++ b/src/kernel/mm.c @@ -328,10 +328,15 @@ void lowlevel_setup(uint64_t phys_off, uint64_t phys_size) ram_phys_off = kCacheableView + phys_off; ram_phys_size = phys_size; - if (!(get_el() == 1)) panic("pongoOS runs in EL1 only! did you skip pongoMon?"); - - set_vbar_el1((uint64_t)&exception_vector); - enable_mmu_el1((uint64_t)ttbr0, 0x13A402A00 | (tg0 << 14) | (tg1 << 30) | (t1sz << 16) | t0sz, 0x04ff00, (uint64_t)ttbr1); + if (get_el() == 2) { + set_vbar_el2((uint64_t)&exception_vector_el2); + enable_mmu_el2((uint64_t)ttbr0, 0x13A402A00 | (tg0 << 14) | (tg1 << 30) | (t1sz << 16) | t0sz, 0x04ff00, (uint64_t)ttbr1); + } else if (get_el() == 1) { + set_vbar_el1((uint64_t)&exception_vector); + enable_mmu_el1((uint64_t)ttbr0, 0x13A402A00 | (tg0 << 14) | (tg1 << 30) | (t1sz << 16) | t0sz, 0x04ff00, (uint64_t)ttbr1); + } else { + panic("pongoOS runs in EL1/2 only! did you skip pongoMon?"); + } kernel_vm_space.ttbr0 = (uint64_t)ttbr0; kernel_vm_space.ttbr1 = (uint64_t)ttbr1; @@ -344,7 +349,13 @@ void lowlevel_set_identity(void) void lowlevel_cleanup(void) { cache_clean_and_invalidate((void*)ram_phys_off, ram_phys_size); - disable_mmu_el1(); + if (get_el() == 2) { + disable_mmu_el2(); + } else if (get_el() == 1) { + disable_mmu_el1(); + } else { + panic("pongoOS runs in EL1/2 only! did you skip pongoMon?"); + } } struct vm_space* task_vm_space(struct task* task) { return task->vm_space; @@ -556,6 +567,7 @@ void asid_free(uint64_t asid) { fiprintf(stderr, "freeing asid: %llx\n", asid); #endif asid_table[index >> 3] &= ~(1 << (index&0x7)); + asm volatile("ISB"); asm volatile("TLBI ASIDE1IS, %0" : : "r"(asid)); asm volatile("DSB SY"); @@ -790,16 +802,24 @@ void phys_dereference(uint64_t pa, uint64_t size) { } enable_interrupts(); } - +uint64_t gMemSize = 0; void alloc_init() { if (alloc_static_base) return; uint64_t memory_size = gBootArgs->memSize; + if (memory_size > 0x100000000) memory_size = 0x100000000; // force pongo to use at most 4GB (FIXME: remove when we support >4G ram) + gMemSize = memory_size; + + uint64_t pbase = gBootArgs->physBase; + memory_size -= pbase - 0x800000000; + ppages = memory_size >> 14; uint64_t early_heap = early_heap_base; + early_heap = ((early_heap + 0x3fff) & (~0x3fff)); + #ifdef AUTOBOOT - uint64_t* _autoboot_block = (uint64_t*)0x419000000; + uint64_t* _autoboot_block = (uint64_t*)(kCacheableView + 0x19000000); extern uint64_t* autoboot_block; if (_autoboot_block[0] == 0x746F6F626F747561) { autoboot_block = (void*) early_heap; @@ -819,7 +839,7 @@ void alloc_init() { } alloc_static_current = alloc_static_base = (kCacheableView - 0x800000000 + gBootArgs->topOfKernelData) & (~0x3fff); - alloc_static_end = 0x417fe0000; + alloc_static_end = (kCacheableView + 0x17fe0000); uint64_t alloc_static_hardcap = alloc_static_base + (1024 * 1024 * 64); if (alloc_static_end > alloc_static_hardcap) { phys_force_free(vatophys_static((void*)alloc_static_hardcap), alloc_static_end - alloc_static_hardcap); @@ -827,7 +847,7 @@ void alloc_init() { } uint64_t alloc_heap_base = (((uint64_t)early_heap) + 0x7fff) & (~0x3fff); - uint64_t alloc_heap_end = (((uint64_t)(phystokv(gBootArgs->physBase) + gBootArgs->memSize)) + 0x3fff) & (~0x3fff) - 1024*1024; + uint64_t alloc_heap_end = (((uint64_t)(phystokv(gBootArgs->physBase) + memory_size)) + 0x3fff) & (~0x3fff) - 1024*1024; phys_force_free(vatophys_static((void*)alloc_heap_base), alloc_heap_end - alloc_heap_base); } @@ -895,8 +915,8 @@ void* phystokv(uint64_t paddr) { } uint64_t vatophys_static(void* kva) { uint64_t kva_check = (uint64_t) kva; - if (!((kva_check >= kCacheableView) && (kva_check < (kCacheableView + 0x100000000)))) { - panic("vatophys_static must be called on kCacheableView map addresses"); + if (!((kva_check >= kCacheableView) && (kva_check < (kCacheableView + gMemSize)))) { + panic("vatophys_static must be called on kCacheableView map addresses! addr = %llx", kva_check); } return (((uint64_t)kva) - kCacheableView + 0x800000000); } diff --git a/src/kernel/pongo.h b/src/kernel/pongo.h index 289507f5..5f168693 100644 --- a/src/kernel/pongo.h +++ b/src/kernel/pongo.h @@ -34,22 +34,6 @@ #include #include -#ifdef PONGO_PRIVATE -#include "framebuffer/fb.h" -#include "usb/usb.h" -#include "uart/uart.h" -#include "gpio/gpio.h" -#include "timer/timer.h" -#include "xnu/xnu.h" -#include "tz/tz.h" -#include "libDER/DER_Encode.h" -#include "libDER/DER_Decode.h" -#include "libDER/asn1Types.h" -#include "libDER/oids.h" -#include "mipi/mipi.h" -#include "aes/aes.h" -#endif - #define DT_KEY_LEN 0x20 extern int service_cmd(const char* name, int cmd_id, void* data_in, size_t in_size, void* data_out, size_t* out_size); @@ -123,6 +107,12 @@ extern volatile char gBootFlag; #define BOOT_FLAG_HOOK 2 #define BOOT_FLAG_LINUX 3 #define BOOT_FLAG_RAW 4 +#define BOOT_FLAG_JUMP 5 +extern uint64_t gBootJumpTo; +extern uint64_t gBootJumpArgs[4]; +extern uint64_t gBootJumpToReloc; +extern uint64_t gBootJumpToRelocSize; +extern uint64_t gBootJumpToRelocFrom; #define LINUX_DTREE_SIZE 262144 #define LINUX_CMDLINE_SIZE 4096 @@ -134,6 +124,7 @@ extern void lock_release(lock* lock); // releases ownership on a lock extern int dt_check(void* mem, uint32_t size, uint32_t* offp); extern int dt_parse(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, dt_node_t*), void* cbn_arg, int (*cb_prop)(void*, dt_node_t*, int, const char*, void*, uint32_t), void* cbp_arg); +extern int dt_parse_ex(dt_node_t* node, int depth, uint32_t* offp, int (*cb_node)(void*, dt_node_t*), void* cbn_arg, int (*cb_prop)(void*, dt_node_t*, int, const char*, void*, uint32_t), void* cbp_arg, uint32_t flags); extern dt_node_t* dt_find(dt_node_t* node, const char* name); extern void* dt_prop(dt_node_t* node, const char* key, uint32_t* lenp); extern void* dt_get_prop(const char* device, const char* prop, uint32_t* size); @@ -225,6 +216,8 @@ extern void proc_reference(struct proc*); extern void proc_release(struct proc*); extern struct task* proc_create_task(struct proc* proc, void* entrypoint); #define PROC_NO_VM 1 +extern void fb_reset_cursor(); +extern void task_exit_irq_continuation(struct task* continue_to_task, struct hal_device* spoofed_irq_controller, uint32_t spoofed_irq_type, void* spoofed_context); extern uint32_t loader_xfer_recv_size; extern void resize_loader_xfer_data(uint32_t newsz); extern bool vm_fault(struct vm_space* vmspace, uint64_t vma, vm_protect_t fault_prot); @@ -264,6 +257,11 @@ extern void mark_phys_wired(uint64_t pa, uint64_t size); extern void phys_force_free(uint64_t pa, uint64_t size); extern void phys_reference(uint64_t pa, uint64_t size); extern void phys_dereference(uint64_t pa, uint64_t size); +extern void interrupt_associate_context(uint32_t irqno, void* context); +extern void* interrupt_context(uint32_t irqno); +extern void* task_interrupt_context(); +extern void* task_current_interrupt_context(); +extern uint64_t gIORVBAR; typedef struct xnu_pf_range { uint64_t va; @@ -332,6 +330,7 @@ extern uint64_t xnu_rebase_va(uint64_t va); extern uint64_t kext_rebase_va(uint64_t va); extern struct mach_header_64* xnu_pf_get_kext_header(struct mach_header_64* kheader, const char* kext_bundle_id); extern void xnu_pf_apply_each_kext(struct mach_header_64* kheader, xnu_pf_patchset_t* patchset); +extern const char* device_clock_name_by_id(uint32_t idx); #ifdef OVERRIDE_CACHEABLE_VIEW # define kCacheableView OVERRIDE_CACHEABLE_VIEW @@ -387,7 +386,7 @@ extern void event_wait_asserted(struct event* ev); extern void event_wait(struct event* ev); extern void event_fire(struct event* ev); extern void* alloc_static(uint32_t size); // memory returned by this will be added to the xnu static region, thus will persist after xnu boot -extern void task_bind_to_irq(struct task* task, int irq); +extern void task_bind_to_irq(struct task* task, uint32_t irq); extern struct event command_handler_iter; #ifdef memset @@ -439,7 +438,7 @@ extern void* phystokv(uint64_t paddr); extern void cache_invalidate(void *address, size_t size); extern void cache_clean_and_invalidate(void *address, size_t size); extern void cache_clean(void *address, size_t size); -extern void register_irq_handler(uint16_t irq_v, struct task* irq_handler); +extern void register_irq_handler(uint32_t irq_v, struct task* irq_handler); extern uint64_t device_clock_by_id(uint32_t id); extern uint64_t device_clock_by_name(const char *name); extern void clock_gate(uint64_t addr, char val); @@ -475,6 +474,8 @@ extern void interrupt_init(); extern void interrupt_teardown(); extern void task_irq_teardown(); extern uint32_t exception_vector[]; +extern uint32_t exception_vector_el2[]; +extern void set_vbar_el2(uint64_t vec); extern void set_vbar_el1(uint64_t vec); extern void rebase_pc(uint64_t vec); extern void rebase_sp(uint64_t vec); @@ -484,6 +485,8 @@ extern uint64_t get_mpidr(void); extern void set_migsts(uint64_t val); extern void enable_mmu_el1(uint64_t ttbr0, uint64_t tcr, uint64_t mair, uint64_t ttbr1); extern void disable_mmu_el1(); +extern void enable_mmu_el2(uint64_t ttbr0, uint64_t tcr, uint64_t mair, uint64_t ttbr1); +extern void disable_mmu_el2(); extern void lowlevel_cleanup(void); extern void lowlevel_setup(uint64_t phys_off, uint64_t phys_size); extern void map_full_ram(uint64_t phys_off, uint64_t phys_size); @@ -494,12 +497,33 @@ static inline _Bool is_16k(void) } static inline void flush_tlb(void) { - __asm__ volatile("isb"); - __asm__ volatile("tlbi vmalle1\n"); - __asm__ volatile("dsb sy"); + if (get_el() == 2) { + __asm__ volatile("isb"); + __asm__ volatile("tlbi alle2\n"); + __asm__ volatile("dsb sy"); + } else { + __asm__ volatile("isb"); + __asm__ volatile("tlbi vmalle1\n"); + __asm__ volatile("dsb sy"); + } } extern void task_real_unlink(struct task* task); + #include "hal/hal.h" +#include "framebuffer/fb.h" +#include "usb/usb.h" +#include "dart/dart.h" +#include "uart/uart.h" +#include "timer/timer.h" +#include "xnu/xnu.h" +#include "tz/tz.h" +#include "libDER/DER_Encode.h" +#include "libDER/DER_Decode.h" +#include "libDER/asn1Types.h" +#include "libDER/oids.h" +#include "mipi/mipi.h" +#include "aes/aes.h" +#include "i2c/i2c.h" #endif diff --git a/src/kernel/task.c b/src/kernel/task.c index 28f158e2..77c26198 100644 --- a/src/kernel/task.c +++ b/src/kernel/task.c @@ -60,10 +60,13 @@ void task_timer_fired() { task_timer_ctr ++; } -struct task* irqvecs[0x200]; -void register_irq_handler(uint16_t irq_v, struct task* irq_handler) +extern struct task** irqvecs; +extern uint32_t irq_count; + +void register_irq_handler(uint32_t irq_v, struct task* irq_handler) { - if (irq_v >= 0x1ff) panic("invalid irq"); + if (irq_v >= irq_count) panic("invalid irq"); + if (irqvecs[irq_v]) task_release(irqvecs[irq_v]); if (irq_handler) task_reference(irq_handler); irqvecs[irq_v] = irq_handler; @@ -93,7 +96,7 @@ void task_list(const char* cmd, char* arg) { } cur_task = cur_task->next; } while (cur_task != &sched_task); - for (int i=0; i<0x1ff; i++) { + for (int i=0; i<0x7ff; i++) { if (irqvecs[i]) { ++nirq; } @@ -129,7 +132,7 @@ retry:; } cur_task = cur_task->next; } while (cur_task != &sched_task); - for (int i=0; i<0x1ff; i++) { + for (int i=0; i<0x7ff; i++) { if (irqvecs[i]) { ++ni; } @@ -166,7 +169,7 @@ retry:; cur_task = cur_task->next; } while (cur_task != &sched_task); ni = 0; - for (int i=0; i<0x1ff; i++) { + for (int i=0; i<0x7ff; i++) { if (irqvecs[i]) { strlcpy(irq_copy[ni].name, irqvecs[i]->name, sizeof(irq_copy[ni].name)); irq_copy[ni].runcnt = irqvecs[i]->irq_count; @@ -197,14 +200,14 @@ retry:; for(int i = 0; i < ntasks; ++i) { task_info_t *t = &tasks_copy[i]; - iprintf(" | %7s | task %d | runcnt = %llx | flags = %s, %s\n", t->name[0] ? t->name : "unknown", t->pid, t->runcnt, t->flags & TASK_PREEMPT ? "preempt" : "coop", t->flags & TASK_LINKED ? "run" : "wait"); + iprintf(" | %13s | task %d | runcnt = %llx | flags = %s, %s\n", t->name[0] ? t->name : "unknown", t->pid, t->runcnt, t->flags & TASK_PREEMPT ? "preempt" : "coop", t->flags & TASK_LINKED ? "run" : "wait"); } iprintf("=+= IRQ Handlers ===\n"); for(int i = 0; i < nirq; ++i) { task_info_t *t = &irq_copy[i]; char* nm = t->name[0] ? t->name : "unknown"; - iprintf(" | %7s (%d) | runcnt: %lld | irq: %d | irqcnt: %llu | flags: %s, %s\n", nm, t->pid, t->runcnt, i, t->irq_count, t->flags & TASK_PREEMPT ? "preempt" : "coop", t->flags & TASK_LINKED ? "run" : "wait"); + iprintf(" | %13s (%d) | runcnt: %lld | irq: %d | irqcnt: %llu | flags: %s, %s\n", nm, t->pid, t->runcnt, i, t->irq_count, t->flags & TASK_PREEMPT ? "preempt" : "coop", t->flags & TASK_LINKED ? "run" : "wait"); } iprintf("=+= Loaded modules ===\n"); extern void pongo_module_print_list(); @@ -214,13 +217,17 @@ retry:; free(irq_copy); } void task_irq_teardown() { - for (int i=0; i<0x1ff; i++) { + for (int i=0; iirq_context; +} __attribute__((noinline)) void task_switch_irq(struct task* new) { if (!(new->flags & TASK_IRQ_HANDLER)) panic("we are not entering an irq task"); @@ -237,25 +244,43 @@ __attribute__((noinline)) void task_switch_irq(struct task* new) _task_switch(new); if (new->irq_ret != task_current()) { puts("======== IRQ SCHEDULER FAULT ========"); - iprintf("cur_task: %p != irq_ret: %p\n", task_current(), new->irq_ret); + fiprintf(stderr, "cur_task: %p != irq_ret: %p\n", task_current(), new->irq_ret); panic("task_current() is not set correctly"); } new->irq_ret = NULL; if (new->flags & TASK_MASK_NEXT_IRQ) new->flags &= ~TASK_MASK_NEXT_IRQ; - else unmask_interrupt(new->irq_type); // re-arm IRQ + else { + if (!new->irq_controller || new->irq_controller == gInterruptDevice) { // fastpath + unmask_interrupt(new->irq_type); + } else { + hal_controller_unmask_interrupt(new->irq_controller, new->irq_type); // re-arm IRQ + } + } served_irqs++; + + struct task* continuation = new->irq_continue; + if (continuation) { + new->irq_continue = NULL; + new->irq_context = NULL; + new->irq_controller = NULL; + return task_switch_irq(continuation); + } } -__attribute__((noinline)) void task_irq_dispatch(uint32_t intr) { - struct task* irq_handler = irqvecs[intr & 0x1FF]; +__attribute__((noinline)) void task_irq_dispatch(uint32_t intr) { // AIC irq dispatch + if (intr > irq_count) { + panic("task_irq_dispatch: irqno out of bounds (%d > %d)", intr, irq_count); + } + struct task* irq_handler = irqvecs[intr]; if (irq_handler) { - irq_handler->irq_type = intr & 0x1ff; + irq_handler->irq_controller = gInterruptDevice; // AIC + irq_handler->irq_type = intr; + irq_handler->irq_context = interrupt_context(intr); task_switch_irq(irq_handler); } else { - iprintf("couldn't find irq handler for %x\n", intr); + fiprintf(stderr, "couldn't find irq handler for %x\n", intr); panic("task_irq_dispatch"); } } - extern struct task sched_task; extern uint32_t preempt_ctr; void task_yield_preemption() { @@ -393,7 +418,7 @@ void task_entry() { } if (task->vm_space == &kernel_vm_space) { - task->cpsr = 0x4; // EL1 SP0 + task->cpsr = get_el() << 2; // ELn SP0 void (*entry)() = (void*)task->entry; task_entry_j(entry, task->entry_stack, task_exit, task->cpsr); @@ -595,7 +620,7 @@ struct task* task_create_extended(const char* name, void (*entry)(), int task_ty return task; } -void task_bind_to_irq(struct task* task, int irq) { +void task_bind_to_irq(struct task* task, uint32_t irq) { disable_interrupts(); register_irq_handler(irq, task); unmask_interrupt(irq); @@ -643,9 +668,15 @@ void task_exit_irq() if (task_current()->flags & TASK_PREEMPT) { disable_interrupts(); if (!(task_current()->flags & TASK_LINKED)) panic("task_exit_irq on unlinked preempt irq handler?"); - task_unlink(task_current()); if (task_current()->flags & TASK_MASK_NEXT_IRQ) task_current()->flags &= ~TASK_MASK_NEXT_IRQ; - else unmask_interrupt(task_current()->irq_type); // re-arm IRQ + else { + if (!task_current()->irq_controller || task_current()->irq_controller == gInterruptDevice) { + unmask_interrupt(task_current()->irq_type); + } else { + hal_controller_unmask_interrupt(task_current()->irq_controller, task_current()->irq_type); // re-arm IRQ + } + } + task_unlink(task_current()); if (dis_int_count != 1) { panic("irq handler yielded with interrupts held"); } @@ -655,6 +686,21 @@ void task_exit_irq() if (!task_current()->irq_ret) panic("task_exit_irq must be invoked from enabled irq context"); _task_switch(task_current()->irq_ret); } +void task_exit_irq_continuation(struct task* continue_to_task, struct hal_device* spoofed_irq_controller, uint32_t spoofed_irq_type, void* spoofed_context) { + if (task_current()->flags & TASK_PREEMPT) { + panic("task_irq_continuation must be invoked on cooperative IRQ handlers only!"); + } + + continue_to_task->irq_controller = spoofed_irq_controller; + continue_to_task->irq_type = spoofed_irq_type; + continue_to_task->irq_context = spoofed_context; + task_current()->irq_continue = continue_to_task; + + if (!(task_current()->flags & TASK_IRQ_HANDLER)) return task_yield(); + if (!task_current()->irq_ret) panic("task_exit_irq must be invoked from enabled irq context"); + _task_switch(task_current()->irq_ret); +} + extern uint64_t dis_int_count; void task_switch(struct task* new) { diff --git a/src/kernel/task.h b/src/kernel/task.h index 0af72478..e5a18681 100644 --- a/src/kernel/task.h +++ b/src/kernel/task.h @@ -128,6 +128,9 @@ struct task { // a task is a thread-like execution environment, executing under lock task_lock; struct proc* proc; struct task* proc_task_list_next; // only tasks created with proc_create_task are queued here + struct task* irq_continue; + void* irq_context; + struct hal_device* irq_controller; }; extern void task_alloc_fast_stacks(struct task* task); diff --git a/src/shell/autoboot.c b/src/shell/autoboot.c index 587e337e..99c64334 100644 --- a/src/shell/autoboot.c +++ b/src/shell/autoboot.c @@ -34,7 +34,7 @@ void pongo_autoboot() memcpy(loader_xfer_recv_data, &autoboot_block[2], (uint32_t)autoboot_block[1]); loader_xfer_recv_count = (uint32_t)autoboot_block[1]; autoboot_count = loader_xfer_recv_count; - phys_force_free(vatophys((uint64_t)autoboot_block), (autoboot_block[1] + 0x20 + 0x3fff) & ~0x3fff); + //phys_force_free(vatophys((uint64_t)autoboot_block), (autoboot_block[1] + 0x20 + 0x3fff) & ~0x3fff); queue_rx_string("modload\nautoboot\n"); } diff --git a/src/shell/main.c b/src/shell/main.c index 5feb4ed0..cf96b1b3 100644 --- a/src/shell/main.c +++ b/src/shell/main.c @@ -290,4 +290,7 @@ void shell_main() { task_register(&pongo_lock_test2, pongo_lock_test2_entry); #endif // gBootFlag = BOOT_FLAG_HOOK; + + sleep(5); + queue_rx_string("ps\n"); } diff --git a/tools/machopack.c b/tools/machopack.c new file mode 100644 index 00000000..cc53b07c --- /dev/null +++ b/tools/machopack.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../apple-include/mach-o/loader.h" + +int main(int argc, char** argv) { + if (argc != 3 && argc != 4) { + puts("usage: machopack [in (PongoConsolidated.bin)] [out (PongoAppleSilicon)] [optional, module to autoboot]"); + return -1; + } + + int pongo = open(argv[1], O_RDONLY); + if (pongo < 0) { + puts("in file could not be opened!"); + return -2; + } + int out = open(argv[2], O_RDWR | O_TRUNC | O_CREAT, 0755); + if (out < 0) { + puts("out file could not be opened!"); + return -2; + } + struct stat ms; + int module = -1; + + uint64_t pongoimgsz = 0; + uint32_t modulesize = 0; + + if (argc == 4) { + module = open(argv[3], O_RDONLY); + if (module < 0) { + puts("module file could not be opened!"); + return -3; + } + + if (fstat(module, &ms) == -1) { + perror("fstat"); + return -4; + } + + pongoimgsz += ms.st_size + 0x20; + modulesize = ms.st_size + 0x20; + } + + struct stat s; + if (fstat(pongo, &s) == -1) { + perror("fstat"); + return -4; + } + pongoimgsz += s.st_size; + + struct mach_header_64 mh = {0}; + mh.magic = MH_MAGIC_64; + mh.cputype = CPU_TYPE_ARM64; + mh.cpusubtype = 0x00000002; + mh.filetype = 12; + mh.ncmds = 3; + mh.flags = MH_DYLDLINK; + + uint32_t siz = 0; + + struct { + struct segment_command_64 sh; + struct segment_command_64 sc; + struct section_64 se[1]; + } sc = {0}; + +#define VPOFFS (0xfffffe0004000000ull - 0x800000000ull) +#define BASE_ADDR (0x818000000 + VPOFFS) + + sc.sh.cmd = LC_SEGMENT_64; + sc.sh.cmdsize = sizeof(struct segment_command_64); + sc.sh.initprot = 1; + sc.sh.maxprot = 1; + sc.sh.nsects = 0; + sc.sh.fileoff = 0; + sc.sh.filesize = 0x4000; + sc.sh.vmsize = 0x4000; + sc.sh.vmaddr = BASE_ADDR; + strcpy(sc.sh.segname, "__HEADER"); + + sc.sc.cmd = LC_SEGMENT_64; + sc.sc.cmdsize = sizeof(struct section_64) + sizeof(struct segment_command_64); + sc.sc.initprot = 7; + sc.sc.maxprot = 7; + sc.sc.nsects = 1; + sc.sc.fileoff = 0x4000; + sc.sc.filesize = pongoimgsz; + sc.sc.vmsize = pongoimgsz + (128 * 1024 * 1024); // reserve at least 64M of kerneldata + sc.sc.vmsize += 0x3fff; + sc.sc.vmsize &= ~0x3fff; + sc.sc.vmaddr = BASE_ADDR + 0x4000; + strcpy(sc.sc.segname, "__TEXT"); + + sc.se[0].flags = S_ATTR_SOME_INSTRUCTIONS; + sc.se[0].align = 16; + sc.se[0].addr = sc.sc.vmaddr; + sc.se[0].size = pongoimgsz; + sc.se[0].offset = 0x4000; + strcpy(sc.se[0].sectname, "__pongo"); + strcpy(sc.se[0].segname, "__TEXT"); + + siz += sizeof(sc); + + struct __attribute__ ((packed)) __arm_thread_state64 { + uint64_t x[29]; + uint64_t fp; + uint64_t lr; + uint64_t sp; + uint64_t pc; + uint32_t cpsr; + uint32_t _pad; + }; + + struct thread_command_arm64 { + uint32_t cmd; /* LC_THREAD or LC_UNIXTHREAD */ + uint32_t cmdsize; /* total size of this command */ + uint32_t flavor; /* flavor of thread state */ + uint32_t count; /* count of uint32_t's in thread state */ + struct __arm_thread_state64 s64; + }; + + struct thread_command_arm64 tc = { 0 }; + + tc.cmd = LC_UNIXTHREAD; + tc.cmdsize = sizeof(tc); + tc.flavor = ARM_THREAD_STATE64; + tc.count = 68; + tc.s64.pc = sc.se[0].addr + 0x1100; // pongo entrypoint for macho is at +0x1004 in order to leave space for IORVBAR + + siz += sizeof(tc); + + mh.sizeofcmds = siz; + + write(out, &mh, sizeof(mh)); + write(out, &sc, sizeof(sc)); + write(out, &tc, sizeof(tc)); + + lseek(out, 0x4000, SEEK_SET); + + char* buf = malloc(s.st_size); + assert(s.st_size == read(pongo, buf, s.st_size)); + write(out, buf, s.st_size); + free(buf); + + if (module != -1) { + uint64_t autoboot_block[2] = {0x746F6F626F747561, ms.st_size}; + + write(out, &autoboot_block, 0x10); + + char* buf = malloc(ms.st_size); + assert(ms.st_size == read(module, buf, ms.st_size)); + write(out, buf, ms.st_size); + free(buf); + } + + return 0; +}