1/**
2 * \file acpi_generic.c
3 * \brief
4 */
5
6
7/*
8 * Copyright (c) 2017 ETH Zurich.
9 * All rights reserved.
10 *
11 * This file is distributed under the terms in the attached LICENSE file.
12 * If you do not find this file, copies can be found by writing to:
13 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
14 */
15
16// Memory allocator instance for physical address regions and platform memory
17
18#include <barrelfish/barrelfish.h>
19#include <barrelfish/capabilities.h>
20#include <if/monitor_blocking_defs.h>
21
22#include <mm/mm.h>
23
24#include <skb/skb.h>
25
26#include "acpi_debug.h"
27#include "acpi_shared.h"
28#include "acpi_allocators.h"
29
30
31struct mm pci_mm_physaddr;
32
33struct capref physical_caps;
34struct capref my_devframes_cnode;
35
36// XXX should be from offests
37#define PADDR_SPACE_SIZE_BITS   48
38#define MAXCHILDBITS    4               ///< Max branching of BTree nodes
39
40errval_t acpi_allocators_init(void)
41{
42    errval_t err, msgerr;
43
44    ACPI_DEBUG("acpi: initializing allocators\n");
45
46    struct monitor_blocking_binding *cl = get_monitor_blocking_binding();
47    assert(cl != NULL);
48
49    ACPI_DEBUG("acpi: obtaining boot info...\n");
50
51    // Get the bootinfo and map it in.
52    struct capref bootinfo_frame;
53    size_t bootinfo_size;
54    struct bootinfo *bootinfo;
55
56    err = slot_alloc(&bootinfo_frame);
57    if (err_is_fail(err)) {
58        USER_PANIC_ERR(err, "slot_alloc for monitor->get_bootinfo");
59    }
60
61    msgerr = cl->rpc_tx_vtbl.get_bootinfo(cl, &err, &bootinfo_frame, &bootinfo_size);
62    if (err_is_fail(msgerr) || err_is_fail(err)) {
63        USER_PANIC_ERR(err_is_fail(msgerr) ? msgerr : err, "failed in get_bootinfo");
64    }
65
66    err = vspace_map_one_frame((void**)&bootinfo, bootinfo_size, bootinfo_frame,
67                               NULL, NULL);
68    if (err_is_fail(err)) {
69        DEBUG_ERR(err, "mapping of bootinfo frame failed\n");
70        return err;
71    }
72
73    ACPI_DEBUG("acpi: boot info mapped [%p..%p]\n", bootinfo,
74                bootinfo + bootinfo_size);
75
76
77    ACPI_DEBUG("acpi: setup slot allocator with %" PRIu64 " slots\n", L2_CNODE_SLOTS);
78
79    /* Initialize the memory allocator to handle PhysAddr caps */
80    static struct range_slot_allocator devframes_allocator;
81    err = range_slot_alloc_init(&devframes_allocator, L2_CNODE_SLOTS, NULL);
82    if (err_is_fail(err)) {
83        return err_push(err, LIB_ERR_SLOT_ALLOC_INIT);
84    }
85
86    err = range_slot_alloc_refill(&devframes_allocator, L2_CNODE_SLOTS);
87    if (err_is_fail(err)) {
88        return err_push(err, LIB_ERR_SLOT_ALLOC_INIT);
89    }
90    ACPI_DEBUG("acpi: setup memory manager for physaddr region\n");
91
92    err = mm_init(&pci_mm_physaddr, ObjType_DevFrame, 0, 48,
93                  /* This next parameter is important. It specifies the maximum
94                   * amount that a cap may be "chunked" (i.e. broken up) at each
95                   * level in the allocator. Setting it higher than 1 reduces the
96                   * memory overhead of keeping all the intermediate caps around,
97                   * but leads to problems if you chunk up a cap too small to be
98                   * able to allocate a large subregion. This caused problems
99                   * for me with a large framebuffer... -AB 20110810 */
100                  1, /*was DEFAULT_CNODE_BITS,*/
101                  slab_default_refill, slot_alloc_dynamic,
102                  slot_refill_dynamic, &devframes_allocator, false);
103    if (err_is_fail(err)) {
104        return err_push(err, MM_ERR_MM_INIT);
105    }
106
107
108    ACPI_DEBUG("acpi: REFILL=%p %p\n", slot_refill_dynamic, pci_mm_physaddr.slot_refill);
109
110    struct capref requested_caps;
111
112
113    // XXX: The code below is confused about gen/l/paddrs.
114    // Caps should be managed in genpaddr, while the bus mgmt must be in lpaddr.
115
116    ACPI_DEBUG("acpi: obtaining CNODE containing physaddr caps\n");
117
118    // Here we get a cnode cap, so we need to put it somewhere in the root cnode
119    // As we already have a reserved slot for a phyaddr caps cnode, we put it there
120    err = slot_alloc(&requested_caps);
121    if (err_is_fail(err)) {
122        USER_PANIC_ERR(err, "slot_alloc for monitor->get_phyaddr_cap");
123    }
124    err = cl->rpc_tx_vtbl.get_phyaddr_cap(cl, &requested_caps, &msgerr);
125    assert(err_is_ok(err) && err_is_ok(msgerr));
126    physical_caps = requested_caps;
127
128    struct capref pacn = {
129        .cnode = cnode_root,
130        .slot = ROOTCN_SLOT_PACN
131    };
132    // Move phyaddr cap to ROOTCN_SLOT_PACN to conform to 2 level cspace
133    err = cap_copy(pacn, requested_caps);
134    assert(err_is_ok(err));
135
136    // Build the capref for the first physical address capability
137    struct capref phys_cap;
138    phys_cap.cnode = build_cnoderef(pacn, CNODE_TYPE_OTHER);
139    phys_cap.slot = 0;
140
141    ACPI_DEBUG("acpi: creating L2 cnode for device caps\n");
142
143    struct cnoderef devcnode;
144    err = cnode_create_l2(&my_devframes_cnode, &devcnode);
145    if (err_is_fail(err)) { USER_PANIC_ERR(err, "cnode create"); }
146    struct capref devframe;
147    devframe.cnode = devcnode;
148    devframe.slot = 0;
149
150    if (bootinfo->regions_length > L2_CNODE_SLOTS) {
151        USER_PANIC("boot info has more regions (%d) than fit into L2 CNode (%d)",
152                bootinfo->regions_length, L2_CNODE_SLOTS);
153    }
154
155    ACPI_DEBUG("acpi: walking boot info to obtain the device regions\n");
156
157    for (int i = 0; i < bootinfo->regions_length; i++) {
158        struct mem_region *mrp = &bootinfo->regions[i];
159
160        if (mrp->mr_type == RegionType_Module) {
161//            ACPI_DEBUG("acpi: region[%u] base=0x%" PRIxGENPADDR ", size=%zu, type=%u\n",
162//                               i, mrp->mr_base, mrp->mrmod_size, mrp->mr_type);
163
164            skb_add_fact("memory_region(16'%" PRIxGENPADDR ",%u,%zu,%u,%tu).",
165                         mrp->mr_base, 0, mrp->mrmod_size, mrp->mr_type,
166                         mrp->mrmod_data);
167        }
168        else {
169        //    ACPI_DEBUG("acpi: region[%u] base=0x%" PRIxGENPADDR ", size=%zu, type=%u\n",
170        //                       i, mrp->mr_base, mrp->mr_bytes, mrp->mr_type);
171
172            skb_add_fact("memory_region(16'%" PRIxGENPADDR ",%u,%zu,%u,%tu).",
173                        mrp->mr_base, 0, mrp->mr_bytes, mrp->mr_type,
174                        mrp->mrmod_data);
175        }
176
177        if (mrp->mr_type == RegionType_PhyAddr ||
178            mrp->mr_type == RegionType_PlatformData) {
179            ACPI_DEBUG("Region %d: %"PRIxGENPADDR" - %"PRIxGENPADDR" (%lu) bytes %s\n",
180                       i, mrp->mr_base, mrp->mr_base + mrp->mr_bytes, mrp->mr_bytes,
181                       mrp->mr_type == RegionType_PhyAddr ?
182                       "physical address" : "platform data");
183
184            err = cap_retype(devframe, phys_cap, 0, ObjType_DevFrame, mrp->mr_bytes, 1);
185            if (err_is_ok(err)) {
186                err = mm_add_multi(&pci_mm_physaddr, devframe, mrp->mr_bytes,
187                        mrp->mr_base);
188                if (err_is_fail(err)) {
189                    USER_PANIC_ERR(err, "adding region %d FAILED\n", i);
190                }
191            } else {
192                if (err_no(err) == SYS_ERR_REVOKE_FIRST) {
193                    printf("cannot retype region %d: need to revoke first; ignoring it\n", i);
194                } else {
195                  USER_PANIC_ERR(err, "error in retype\n");
196                }
197            }
198            devframe.slot++;
199            phys_cap.slot++;
200        }
201    }
202
203    return acpi_allocators_init_arch(bootinfo);
204}
205